In an @angular/upgrade hybrid app, what do downgradeComponent, downgradeInjectable and UpgradeComponent each let you do?
answer
- direction is the name
- downgrade: Angular used from AngularJS
- upgrade: AngularJS used from Angular
- services have a recipe, not an API
basics
~20 sdowngradeComponent wraps an Angular component as an AngularJS directive, downgradeInjectable exposes an Angular service to AngularJS's injector, and UpgradeComponent is a base class for an Angular directive that wraps an AngularJS component. AngularJS services are upgraded with a plain Angular provider.
solid answer
~40 sThe names describe the direction. **`downgradeComponent({component})`** returns a factory you register with `.directive()` on an AngularJS module, so AngularJS templates can use an Angular component; inputs and outputs are bound with Angular-style `[prop]` and `(event)` syntax on kebab-case attributes. **`downgradeInjectable(token)`** returns a factory you register with `.factory()`, so AngularJS code can inject an Angular service by name. **`UpgradeComponent`** goes the other way: you write an Angular `@Directive` that extends it, declares the AngularJS component's inputs and outputs, and calls `super('ng1Name', elementRef, injector)`. There is **no API for upgrading an AngularJS service**: you add an Angular provider whose factory reads it from the AngularJS `$injector`. Each DOM element stays owned by exactly one framework.
code
ts · 13 linesimport { Directive, ElementRef, Injector, input, output } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';
@Directive({ selector: 'legacy-hero-detail' })
export class LegacyHeroDetail extends UpgradeComponent {
// Must mirror the AngularJS component's bindings.
hero = input<{ name: string }>();
deleted = output<void>();
constructor(elementRef: ElementRef, injector: Injector) {
super('legacyHeroDetail', elementRef, injector);
}
}go deeper
Remember the direction rule: downgrade means Angular code used from AngularJS, upgrade means AngularJS code used from Angular.
Explain how each helper is registered, what an UpgradeComponent wrapper must declare, and the provider recipe for AngularJS services.
Anticipate the limits: unsupported directive features, the template cache requirement, and services unavailable before a downgraded module loads.
Decide which direction to bridge first so the hybrid needs as few wrappers as possible over its lifetime.
A hybrid application runs AngularJS and Angular in one page. The `@angular/upgrade/static` package provides the bridges, and the naming follows one rule: **downgrading** makes an Angular thing usable from AngularJS, **upgrading** makes an AngularJS thing usable from Angular. ## The four bridges | Need | Tool | Registered where | | :-- | :-- | :-- | | Use an Angular component in an AngularJS template | `downgradeComponent()` | AngularJS module, `.directive(name, factory)` | | Inject an Angular service into AngularJS code | `downgradeInjectable()` | AngularJS module, `.factory(name, factory)` | | Use an AngularJS component in an Angular template | `UpgradeComponent` (base class) | An Angular `@Directive` you write | | Inject an AngularJS service into Angular code | A provider recipe (no API) | Angular `providers` | ## Downgrading a component `downgradeComponent({component: HeroList})` returns a directive factory. Registering it as `heroList` makes `<hero-list>` usable in AngularJS templates. Options include: - `component`: the Angular component class; - `downgradedModule`: needed only when several Angular modules are downgraded with `downgradeModule()`; - `propagateDigest` (default `true`): whether to run `detectChanges` on the component on every AngularJS `$digest`; with `false`, it is still checked when its inputs change. The older `inputs`, `outputs` and `selectors` options are deprecated and no longer used. ## Downgrading a service `downgradeInjectable(HeroesService)` returns an AngularJS factory. Register it as `.factory('heroesService', ...)`, and AngularJS controllers inject `heroesService` like any other service, receiving the Angular instance. With `downgradeModule()`, the service only exists once its Angular module has been bootstrapped, so it must not be used where that is not guaranteed. ## Upgrading a component `UpgradeComponent` is an `@Directive()` base class. The wrapper you write must: 1. declare the AngularJS component's element **selector** in its own `@Directive`; 2. declare **all inputs and outputs** the AngularJS component expects, with names matching its `<` and `&` bindings; 3. **extend `UpgradeComponent`** and call `super('ng1Hero', elementRef, injector)` with the AngularJS component's registered name. The metadata must be written out because Angular's compiler needs it statically. Limitations of the wrapped AngularJS directive: - it must be a **component-style directive with a template**; `compile` (without `link`), `replace` and `terminal` are not supported; - a `templateUrl` must already be in **`$templateCache`**, because loading templates asynchronously is not supported; - its lifecycle hooks (`$onInit`, `$doCheck`, `$postLink`, `$onChanges`, `$onDestroy`) are called by the wrapper. ## Upgrading a service There is no dedicated API. You write an Angular provider that pulls the service from the AngularJS `$injector`: ```ts export const legacyCartProvider = { provide: LegacyCart, useFactory: (i: any) => i.get('legacyCart'), deps: ['$injector'], }; ``` `UpgradeModule` makes the AngularJS `$injector` available to Angular under the `'$injector'` token, which is what the recipe relies on. ## The ownership rule behind all of this The `UpgradeModule` documentation states the mental model: - **Each DOM element is owned by exactly one framework**, whichever instantiated it. - AngularJS directives always execute inside AngularJS, and Angular components inside Angular, wherever they appear. - For an upgraded or downgraded component, the **host element** belongs to the framework using it, and the **view** belongs to the other one. - So attributes in AngularJS templates are written **kebab-case**, while the binding syntax is still **Angular-style** (`[prop]`, `(event)`). ## Common mistakes - Registering `downgradeComponent`'s factory with `.component()` instead of `.directive()`. - Forgetting to list inputs and outputs on an `UpgradeComponent` wrapper. - Using `templateUrl` in an upgraded component without pre-loading the template cache. - Using a downgraded service outside the part of the app where its Angular module is guaranteed to be bootstrapped.
- What does downgradeComponent's propagateDigest option change?By default (true) the downgraded Angular component runs detectChanges on every AngularJS $digest. Setting it to false skips that, and the component is only checked when one of its inputs changes, which saves work in digest-heavy AngularJS screens.
- Why can an upgraded AngularJS component not use an arbitrary templateUrl?UpgradeComponent compiles the AngularJS template synchronously and does not support loading it asynchronously. A templateUrl works only if that template is already in AngularJS's $templateCache, for example pre-populated at build time.
saying these in an interview costs you the question
- downgradeComponent makes an AngularJS component usable in Angular.
- There is an upgradeInjectable function for AngularJS services.
- An UpgradeComponent wrapper discovers the inputs and outputs by itself.
- A downgraded component's DOM is shared and updated by both frameworks.
- Any AngularJS directive, including replace and terminal ones, can be upgraded.