In @angular/upgrade, how does downgradeModule() differ from UpgradeModule, and when would you choose it?
answer
- lazy versus eager Angular
- zone and digest coupling
- performance versus convenience
- services arrive late
basics
~20 sdowngradeModule() bootstraps the Angular part lazily, when a downgraded component is first needed, and does not tie change detection to AngularJS's digest. It suits AngularJS-led apps with a few heavy Angular screens, at the cost of manual cross-framework notification.
solid answer
~40 s`UpgradeModule` bootstraps Angular up front, starts AngularJS inside the Angular zone, and runs a `$digest` whenever Angular settles, so both frameworks stay in sync automatically but run **more change detection than needed**. `downgradeModule()` returns the name of an AngularJS module that **bootstraps an Angular module on demand**, the first time a downgraded component needs it. It does **not** bootstrap AngularJS in the Angular zone and does **not** run a `$digest` when Angular detects changes; it only runs change detection where it knows it is needed, such as when a downgraded component's inputs change. That improves performance in change-detection-heavy apps but means you notify each framework yourself (`$apply`/`$digest` for AngularJS, `NgZone.run` for Angular). Downgraded services exist only after their module has bootstrapped. The two cannot be combined in one app.
go deeper
Remember that a hybrid can be set up two ways, and that downgradeModule() loads the Angular part lazily.
Explain the two internal differences: no Angular zone for AngularJS and no automatic $digest, and what that means for change detection.
Choose between them for a given app, and name the pitfalls of downgradeModule(): late services, stale boundaries, duplicated root services.
Relate the choice to the migration plan: how long the hybrid will live and whether its performance cost justifies manual notification.
`@angular/upgrade/static` offers two ways to set up a hybrid app. They share the bridging helpers (`downgradeComponent`, `downgradeInjectable`, `UpgradeComponent`) but differ in how tightly they couple the two frameworks. ## Side by side | | `UpgradeModule` | `downgradeModule()` | | :-- | :-- | :-- | | What you write | An Angular `NgModule` importing `UpgradeModule`, bootstrapping AngularJS in `ngDoBootstrap()` | An AngularJS module dependency on the name `downgradeModule(...)` returns | | When Angular starts | Up front, before AngularJS | Lazily, when a downgraded component or injectable first needs it | | Zone | AngularJS is bootstrapped inside the Angular zone | AngularJS is **not** bootstrapped inside the Angular zone | | Digest after Angular changes | Runs `$digest` automatically when Angular settles | Does **not** run one automatically | | Change-detection volume | More runs than necessary, but everything stays in sync | Only where needed, such as input changes on downgraded components | | Downgraded services | Available from the start | Available only after their Angular module is bootstrapped | | Combining | Cannot be used together with `downgradeModule()` | Cannot be used together with `UpgradeModule` | ## How `downgradeModule()` is set up `downgradeModule()` accepts one of: - an **`NgModule` class**, bootstrapped with `platformBrowser`'s `bootstrapModule()`; - a **function** returning a promise of an `NgModuleRef`, called with extra providers the returned injector must expose; - an **`NgModuleFactory`**, which is **deprecated** as an argument. It returns the **name of an AngularJS module**; you add that name to your main AngularJS module's dependencies. The Angular module is bootstrapped **once**, the first time it is needed, and reused afterwards. With several downgraded modules, each downgraded component or injectable must name its module (the `downgradedModule` option), each module is bootstrapped separately, and each is a separate "root", so a service with `providedIn: 'root'` gets **one instance per downgraded module** unless you provide shared ones on the platform. ## What "manual notification" means Because the coupling is looser, the documentation leaves it to you to notify each framework when the other changes data: - to make **AngularJS** notice a change, use `scope.$apply(...)` or `$rootScope.$digest()`; - to make **Angular** notice a change, run the code with `ngZone.run(...)`. ## When to choose which **Choose `downgradeModule()` when:** 1. the app is mostly AngularJS with a few Angular screens or widgets; 2. those Angular parts are only needed on some routes, so loading them lazily saves start-up time; 3. the AngularJS side is digest-heavy and the extra cycles of `UpgradeModule` are measurable; 4. the team can handle the explicit notification points at the boundaries. **Choose `UpgradeModule` when:** 1. Angular and AngularJS parts are interleaved on most screens; 2. you want services to be available everywhere from the start; 3. you prefer correctness by default over squeezing change-detection cost. ## Pitfalls specific to `downgradeModule()` - Using a downgraded service in an AngularJS component that can render **before** the Angular module has been bootstrapped fails; use such services only inside the part of the app that guarantees the module exists. - Forgetting to notify the other framework shows up as **stale views** at the boundary, not as errors. - Multiple downgraded modules silently **duplicate root services** unless shared providers are set up. Both options belong to a package in **maintenance mode** that only receives security fixes, so the choice should be made for the length of a planned migration, not for the long term.
- Why can a downgraded service fail under downgradeModule() but not under UpgradeModule?Under downgradeModule() the Angular module that provides the service is bootstrapped lazily, only when a downgraded component or injectable first needs it. An AngularJS component that asks for the service before that point finds nothing, whereas UpgradeModule bootstraps Angular up front.
- With several modules downgraded via downgradeModule(), why might a providedIn: 'root' service exist twice?Each downgraded module is bootstrapped as its own root module, so each gets its own root injector and its own instance of root-provided services. To share one instance, provide it as a static provider on the platform instead.
UpgradeModule is two departments sharing one intercom that rings everyone whenever anyone speaks; nobody misses news, but everyone gets interrupted. downgradeModule() gives each department its own phone: calls are only made when someone knows the other side needs to hear, and forgetting to call leaves them out of date.
saying these in an interview costs you the question
- downgradeModule() is just UpgradeModule with a different import.
- downgradeModule() runs an AngularJS digest after every Angular change.
- Downgraded services are available immediately with downgradeModule().
- You can combine UpgradeModule and downgradeModule() for flexibility.
- Passing an NgModuleFactory is the recommended way to call downgradeModule().