PR Close #18487 PR Close #18487
This commit is contained in:
parent
bb6b59128f
commit
22758912a0
|
@ -1,357 +0,0 @@
|
||||||
# Upgrading from AngularJS (in a more flexible way)
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
_Angular_ is the name for the Angular of today and tomorrow.<br />
|
|
||||||
_AngularJS_ is the name for all 1.x versions of Angular.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
This guide describes some of the built-in tools for efficiently migrating AngularJS projects over
|
|
||||||
to the Angular platform, one piece at a time. It is very similar to the
|
|
||||||
[main upgrade guide](guide/upgrade) with the exception that this one uses the {@link downgradeModule
|
|
||||||
downgradeModule()} helper function instead of the {@link UpgradeModule UpgradeModule} class. This
|
|
||||||
affects how the application is bootstrapped and how change detection is propagated between the two
|
|
||||||
frameworks (more on that later).
|
|
||||||
|
|
||||||
|
|
||||||
## Preparation
|
|
||||||
|
|
||||||
Before we start discussing how you can use `downgradeModule()` to create hybrid applications, there
|
|
||||||
are things that you can do to ease the upgrade process even before you begin upgrading. Although not
|
|
||||||
strictly necessary, preparation goes a long way! The steps are the same regardless how you upgrade,
|
|
||||||
so go ahead and read the [Preparation](guide/upgrade#preparation) section of the main upgrade guide.
|
|
||||||
|
|
||||||
|
|
||||||
## Upgrading with ngUpgrade
|
|
||||||
|
|
||||||
With the ngUpgrade library in Angular you can upgrade an existing AngularJS application
|
|
||||||
incrementally, by building a hybrid application where you can run both frameworks side-by-side. In
|
|
||||||
these hybrid applications you can mix and match AngularJS and Angular components and services and
|
|
||||||
have them interoperate seamlessly. That means you don't have to do the upgrade work all at once,
|
|
||||||
since there is a natural coexistence between the two frameworks during the transition period.
|
|
||||||
|
|
||||||
|
|
||||||
### How ngUpgrade Works
|
|
||||||
|
|
||||||
Regardless of whether you choose `downgradeModule()` or `UpgradeModule`, the basic principles of
|
|
||||||
upgrading, the mental model behind hybrid applications and how you use the {@link upgrade/static
|
|
||||||
upgrade/static} utilities remain the same. You can read about all that in the
|
|
||||||
[How ngUpgrade Works](guide/upgrade#how-ngupgrade-works) section of the main upgrade guide.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
The [Change Detection](guide/upgrade#change-detection) sub-section only applies to applications
|
|
||||||
that use `UpgradeModule`. Change detection is handled differently with `downgradeModule()`.<br />
|
|
||||||
We still recommend reading the sub-section in order to better understand the differences and their
|
|
||||||
implications.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
#### Change Detection with `downgradeModule()`
|
|
||||||
|
|
||||||
As mentioned before, one of the key differences between `downgradeModule()` and `UpgradeModule` has
|
|
||||||
to do with change detection and how it is propagated between the two frameworks.
|
|
||||||
|
|
||||||
With `UpgradeModule`, the two change detection systems are tied together more tightly. Whenever
|
|
||||||
something happens in the AngularJS part of the application, change detection is automatically
|
|
||||||
triggered on the Angular part and vice versa. This is convenient as it ensures that no important
|
|
||||||
change is missed by either framework. Most of the time, though, these extra change detection runs
|
|
||||||
are unnecesary.
|
|
||||||
|
|
||||||
`downgradeModule()`, on the other side, avoids explicitly triggering change detection, unless it
|
|
||||||
knows the other part of the application is interested in the changes. One way to know, for example,
|
|
||||||
is when a value is bound to the {@link Input input} of a downgraded component. If the component
|
|
||||||
defines an `Input`, chances are it needs to be change-detected when that value changes. Thus,
|
|
||||||
`downgradeComponent()` _will_ automatically trigger change detection on that component.
|
|
||||||
|
|
||||||
In most cases, though, the changes made locally in a particular component are of no interest to the
|
|
||||||
rest of the application. For example, if the user clicks a button that submits a form the component
|
|
||||||
will usually handle the result of this action. That being said, there _are_ cases, where you want to
|
|
||||||
propagate changes to some other part of the application, that may be controlled by the other
|
|
||||||
framework. In such cases, you are responsible for notifying the interested parties, by manually
|
|
||||||
triggering change detection.
|
|
||||||
|
|
||||||
If you want a particular piece of code to trigger change detection in the AngularJS part of the
|
|
||||||
application, you need to wrap it in
|
|
||||||
[scope.$apply(...)](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply). Similarly, for
|
|
||||||
triggering change detection in Angular you would use {@link NgZone#run ngZone.run(...)}.
|
|
||||||
|
|
||||||
In many cases, a few extra change detection runs may not matter much. On larger or
|
|
||||||
change-detection-heavy applications, though, they can have a noticeable impact. By giving you more
|
|
||||||
fine-grained control over the change detection propagation, `downgradeModule()` allows you to
|
|
||||||
achieve better performance for your hybrid applications.
|
|
||||||
|
|
||||||
|
|
||||||
### Using `downgradeModule()`
|
|
||||||
|
|
||||||
Both AngularJS and Angular have their own concept of modules to help organize an application into
|
|
||||||
cohesive blocks of functionality.
|
|
||||||
|
|
||||||
Their details are quite different in architecture and implementation. In AngularJS, you create a
|
|
||||||
module by specifying its name and dependencies with
|
|
||||||
[angular.module()](https://docs.angularjs.org/api/ng/function/angular.module). Then you can add
|
|
||||||
assets using its various methods. In Angular, you create a class adorned with an {@link NgModule
|
|
||||||
NgModule} decorator that describes assets in metadata. The differences blossom from there.
|
|
||||||
|
|
||||||
In a hybrid application you run both frameworks at the same time. This means that you need at least
|
|
||||||
one module each from both AngularJS and Angular.
|
|
||||||
|
|
||||||
For the most part, you specify the modules in the same way as you would for a regular application.
|
|
||||||
Then, you use the `upgrade/static` helpers to let the two frameworks know about assets they can use
|
|
||||||
from each other. This is known as "upgrading" and "downgrading" (more on how this is done later).
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
<b>Definitions:</b>
|
|
||||||
|
|
||||||
- _Upgrading_: The act of making an AngularJS asset (e.g. component or service) available to the
|
|
||||||
Angular part of the application.
|
|
||||||
- _Downgrading_: The act of making an Angular asset (e.g. component or service) available to the
|
|
||||||
AngularJS part of the application.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
An important part of inter-linking dependencies, is linking the two main modules together. This is
|
|
||||||
where `downgradeModule()` comes in. It is used to create an AngularJS module — one that you
|
|
||||||
can use as a dependency in your main AngularJS module — that will bootstrap your main Angular
|
|
||||||
module and kick off the Angular part of the hybrid application. In a sense, it takes an Angular
|
|
||||||
module and "downgrades" it to an AngularJS module.
|
|
||||||
|
|
||||||
There are a few things to note, though:
|
|
||||||
|
|
||||||
1. You don't pass the Angular module directly to `downgradeModule()`. All `downgradeModule()` needs
|
|
||||||
is a "recipe" (e.g. a factory function) for creating an instance for your module.
|
|
||||||
|
|
||||||
2. The Angular module is not instantiated until it is actually needed.
|
|
||||||
|
|
||||||
We will expand on these two points below. For now, let's see how we can use `downgradeModule()` to
|
|
||||||
link the two modules.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Import `downgradeModule()`.
|
|
||||||
import { downgradeModule } from '@angular/upgrade/static';
|
|
||||||
|
|
||||||
// Use it to "downgrade" the Angular module to an AngularJS module.
|
|
||||||
const downgradedModule = downgradeModule(MainAngularModuleFactory);
|
|
||||||
|
|
||||||
// Use the downgraded module as a dependency to the main AngularJS module.
|
|
||||||
angular.module('mainAngularJsModule', [
|
|
||||||
downgradedModule
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Specifying a factory for the Angular module
|
|
||||||
|
|
||||||
As mentioned before, `downgradeModule()` needs to know how to instantiate the Angular module. It
|
|
||||||
needs a "recipe". You define that recipe, by providing a factory function that can create an
|
|
||||||
instance of the Angular module. `downgradeModule()` accepts two types of factory functions:
|
|
||||||
|
|
||||||
1. {@link NgModuleFactory NgModuleFactory}
|
|
||||||
2. (extraProviders: {@link StaticProvider StaticProvider}[]) => Promise<{@link NgModuleRef NgModuleRef}>
|
|
||||||
|
|
||||||
If you pass an `NgModuleFactory`, it will be used to instantiate the module using
|
|
||||||
{@link platformBrowser platformBrowser}'s {@link PlatformRef#bootstrapModuleFactory
|
|
||||||
bootstrapModuleFactory()}. This is great, because it is compatible with Ahead-of-Time (AoT)
|
|
||||||
compilation. You can read more about AoT compilation and how to create an `NgModuleFactory` in the
|
|
||||||
[AoT Compilation](guide/aot-compiler) guide.
|
|
||||||
|
|
||||||
Alternatively, you can pass a plain function, which is expected to return a promise resolving to an
|
|
||||||
{@link NgModuleRef NgModuleRef} (i.e. an instance of your Angular module). The function is called
|
|
||||||
with an array of extra {@link StaticProvider Providers} that are expected to be available on the returned
|
|
||||||
`NgModuleRef`'s {@link Injector Injector}. For example, if you are using {@link platformBrowser
|
|
||||||
platformBrowser} or {@link platformBrowserDynamic platformBrowserDynamic}, you can pass the
|
|
||||||
`extraProviders` array to them:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
|
||||||
const platformRef = platformBrowserDynamic(extraProviders);
|
|
||||||
return platformRef.bootstrapModule(MainAngularModule);
|
|
||||||
};
|
|
||||||
// or
|
|
||||||
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
|
||||||
const platformRef = platformBrowser(extraProviders);
|
|
||||||
return platformRef.bootstrapModuleFactory(MainAngularModuleFactory);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Using an `NgModuleFactory` requires less boilerplate and is a good default option as it supports
|
|
||||||
AoT out-of-the-box. Using a custom function requires slightly more code, but gives you greater
|
|
||||||
flexibility.
|
|
||||||
|
|
||||||
|
|
||||||
#### Instantiating the Angular module "on-demand"
|
|
||||||
|
|
||||||
Another key difference between `downgradeModule()` and `UpgradeModule` is that the latter requires
|
|
||||||
you to instantiate both the AngularJS and Angular modules up-front. This means that you have to pay
|
|
||||||
the cost of instantiating the Angular part of the application, even if you don't use any Angular
|
|
||||||
assets until later. `downgradeModule()` is again less aggressive: It will only instantiate the
|
|
||||||
Angular part when it is required for the first time; i.e. as soon as a downgraded component needs to
|
|
||||||
be created.
|
|
||||||
|
|
||||||
You could go a step further and not even download the code for the Angular part of the application
|
|
||||||
to the user's browser, until it is needed. This is especially useful, when you use Angular on parts
|
|
||||||
of the hybrid application that are not necessary for the initial rendering or are not often reached
|
|
||||||
by the user (or not reached by all types of users).
|
|
||||||
|
|
||||||
A few examples:
|
|
||||||
|
|
||||||
- You use Angular on specific routes only and you don't need it until/if such a route is visited by
|
|
||||||
the user.
|
|
||||||
- You use Angular for features that are only visible to specific types of users (e.g. logged-in
|
|
||||||
users or administrators or VIP members). You don't need to load Angular until a user is
|
|
||||||
authenticated.
|
|
||||||
- You use Angular for a feature that is not critical for the initial rendering of the application
|
|
||||||
and you can afford a small delay in favor of better initial load performance.
|
|
||||||
|
|
||||||
|
|
||||||
### Bootstrapping with `downgradeModule()`
|
|
||||||
|
|
||||||
As you may have guessed, you don't need to change anything in the way you bootstrap your existing
|
|
||||||
AngularJS application. Unlike `UpgradeModule` — which requires some extra steps —
|
|
||||||
`downgradeModule()` is able to take care of bootstrapping the Angular module (as long as you provide
|
|
||||||
the recipe).
|
|
||||||
|
|
||||||
In order to start using any `upgrade/static` APIs, you still need to load the Angular framework (as
|
|
||||||
you would in a normal Angular application). You can see how this can be done with SystemJS by
|
|
||||||
following the instructions in the [Setup](guide/setup) guide, selectively copying code from the
|
|
||||||
[QuickStart github repository](https://github.com/angular/quickstart).
|
|
||||||
|
|
||||||
You also need to install the `@angular/upgrade` package via `npm install @angular/upgrade --save`
|
|
||||||
and add a mapping for the `@angular/upgrade/static` package:
|
|
||||||
|
|
||||||
<code-example title="system.config.js">
|
|
||||||
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
Next, create an `app.module.ts` file and add the following `NgModule` class:
|
|
||||||
|
|
||||||
<code-example title="app.module.ts">
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
BrowserModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class MainAngularModule {
|
|
||||||
// Empty placeholder method to prevent the `Compiler` from complaining.
|
|
||||||
ngDoBootstrap() {}
|
|
||||||
}
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
This bare minimum `NgModule` imports `BrowserModule`, the module every Angular browser-based app
|
|
||||||
must have. It also defines an empty `ngDoBootstrap()` method, to prevent the {@link Compiler
|
|
||||||
Compiler} from complaining. This is necessary, because the module will not have a `bootstrap`
|
|
||||||
declaration on its `NgModule` decorator.
|
|
||||||
|
|
||||||
<div class="alert is-important">
|
|
||||||
|
|
||||||
You do not add a `bootstrap` declaration to the `NgModule` decorator, since AngularJS will own the
|
|
||||||
root template of the application and ngUpgrade will be bootstrapping the necessary components.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
You can now link the AngularJS and Angular modules together using `downgradeModule()`.
|
|
||||||
|
|
||||||
<code-example title="app.module.ts">
|
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
||||||
import { downgradeModule } from '@angular/upgrade/static';
|
|
||||||
|
|
||||||
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
|
||||||
const platformRef = platformBrowserDynamic(extraProviders);
|
|
||||||
return platformRef.bootstrapModule(MainAngularModule);
|
|
||||||
};
|
|
||||||
const downgradedModule = downgradeModule(bootstrapFn);
|
|
||||||
|
|
||||||
angular.module('mainAngularJsModule', [
|
|
||||||
downgradedModule
|
|
||||||
]);
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
Congratulations! You are running a hybrid application! The existing AngularJS code works as before
|
|
||||||
_and_ you are ready to start adding Angular code.
|
|
||||||
|
|
||||||
|
|
||||||
### Using Components and Injectables
|
|
||||||
|
|
||||||
The differences between `downgradeModule()` and `UpgradeModule` end here. The rest of the
|
|
||||||
`upgrade/static` APIs and concepts work in the exact same way for both types of hybrid applications.
|
|
||||||
Head over to the [main upgrade guide](guide/upgrade) to learn about:
|
|
||||||
|
|
||||||
- [Using Angular Components from AngularJS Code](guide/upgrade#using-angular-components-from-angularjs-code)
|
|
||||||
- [Using AngularJS Component Directives from Angular Code](guide/upgrade#using-angularjs-component-directives-from-angular-code)
|
|
||||||
- [Projecting AngularJS Content into Angular Components](guide/upgrade#projecting-angularjs-content-into-angular-components)
|
|
||||||
- [Transcluding Angular Content into AngularJS Component Directives](guide/upgrade#transcluding-angular-content-into-angularjs-component-directives)
|
|
||||||
- [Making AngularJS Dependencies Injectable to Angular](guide/upgrade#making-angularjs-dependencies-injectable-to-angular)
|
|
||||||
- [Making Angular Dependencies Injectable to AngularJS](guide/upgrade#making-angular-dependencies-injectable-to-angularjs)
|
|
||||||
|
|
||||||
<div class="alert is-important">
|
|
||||||
|
|
||||||
While it is possible to downgrade injectables, the downgraded injectables will _not_ be available
|
|
||||||
until the Angular module is instantiated too. In order to be safe, you need to ensure that the
|
|
||||||
downgraded injectables are not used anywhere _outside_ the part of the application that is
|
|
||||||
controlled by Angular.
|
|
||||||
|
|
||||||
For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
|
|
||||||
from Angular components, but it is _not OK_ to use it in an AngularJS component that may be used
|
|
||||||
independently of Angular.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
## Using Ahead-of-Time compilation with hybrid applications
|
|
||||||
|
|
||||||
You can take advantage of Ahead-of-Time (AoT) compilation on hybrid applications just like on any
|
|
||||||
other Angular application. The setup for a hybrid application is mostly the same as described in the
|
|
||||||
[AoT Compilation](guide/aot-compiler) guide save for differences in `index.html` and `main-aot.ts`.
|
|
||||||
|
|
||||||
The `index.html` will likely have script tags loading AngularJS files, so the `index.html` for AoT
|
|
||||||
must also load those files. An easy way to copy them is by adding each to the `copy-dist-files.js`
|
|
||||||
file.
|
|
||||||
|
|
||||||
You will also need to pass the generated `MainAngularModuleFactory` to `downgradeModule()`, instead of
|
|
||||||
the custom bootstrap function:
|
|
||||||
|
|
||||||
<code-example title="app/main-aot.ts">
|
|
||||||
import { downgradeModule } from '@angular/upgrade/static';
|
|
||||||
import { MainAngularModuleNgFactory } from '../aot/app/app.module.ngfactory';
|
|
||||||
|
|
||||||
const downgradedModule = downgradeModule(MainAngularModuleNgFactory);
|
|
||||||
|
|
||||||
angular.module('mainAngularJsModule', [
|
|
||||||
downgradedModule
|
|
||||||
]);
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
And that is all you need to do to get the full benefit of AoT for Angular applications!
|
|
||||||
|
|
||||||
|
|
||||||
## Wrap up
|
|
||||||
|
|
||||||
You have learned how to use the {@link upgrade/static upgrade/static} package to incrementally
|
|
||||||
upgrade existing AngularJS applications at your own pace and without impeding further development of
|
|
||||||
the application for the duration of the upgrade process.
|
|
||||||
|
|
||||||
More specifically, you have seen how you can achieve better performance and greater flexibility in
|
|
||||||
your hybrid applications, by using {@link downgradeModule downgradeModule()} (instead of
|
|
||||||
{@link UpgradeModule UpgradeModule}).
|
|
||||||
|
|
||||||
To summarize, the key differentiating factors of `downgradeModule()` are:
|
|
||||||
|
|
||||||
1. It allows instantiating (or even loading) the Angular part lazily, which improves the initial
|
|
||||||
loading time (and is some cases may waive the cost of running a second framework altogether).
|
|
||||||
2. It improves performance by avoiding unnecessary change detection runs, instead putting more
|
|
||||||
responsibility on the developer.
|
|
||||||
3. It does not require you to change how you bootstrap your AngularJS app.
|
|
||||||
|
|
||||||
Based on that, `downgradeModule()` is a good option for hybrid applications that want keep the
|
|
||||||
AngularJS and Angular parts less coupled. You can still mix and match components and services from
|
|
||||||
both frameworks, but you might need to manually propagate change detection. In return,
|
|
||||||
`downgradeModule()` offers more control and better performance characteristics.
|
|
||||||
|
|
||||||
There is merit in both approaches, so you should always weight the pros and cons before deciding
|
|
||||||
which one better meets the upgrading needs of each project.
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
# Upgrading for Performance
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
_Angular_ is the name for the Angular of today and tomorrow.<br />
|
||||||
|
_AngularJS_ is the name for all 1.x versions of Angular.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
This guide describes some of the built-in tools for efficiently migrating AngularJS projects over to
|
||||||
|
the Angular platform, one piece at a time. It is very similar to
|
||||||
|
[Upgrading from AngularJS](guide/upgrade) with the exception that this one uses the {@link
|
||||||
|
downgradeModule downgradeModule()} helper function instead of the {@link UpgradeModule
|
||||||
|
UpgradeModule} class. This affects how the app is bootstrapped and how change detection is
|
||||||
|
propagated between the two frameworks. It allows you to upgrade incrementally while improving the
|
||||||
|
speed of your hybrid apps and leveraging the latest of Angular in AngularJS apps early in the
|
||||||
|
process of upgrading.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Preparation
|
||||||
|
|
||||||
|
Before discussing how you can use `downgradeModule()` to create hybrid apps, there are things that
|
||||||
|
you can do to ease the upgrade process even before you begin upgrading. Because the steps are the
|
||||||
|
same regardless of how you upgrade, refer to the [Preparation](guide/upgrade#preparation) section of
|
||||||
|
[Upgrading from AngularJS](guide/upgrade).
|
||||||
|
|
||||||
|
|
||||||
|
## Upgrading with `ngUpgrade`
|
||||||
|
|
||||||
|
With the `ngUpgrade` library in Angular you can upgrade an existing AngularJS app incrementally by
|
||||||
|
building a hybrid app where you can run both frameworks side-by-side. In these hybrid apps you can
|
||||||
|
mix and match AngularJS and Angular components and services and have them interoperate seamlessly.
|
||||||
|
That means you don't have to do the upgrade work all at once as there is a natural coexistence
|
||||||
|
between the two frameworks during the transition period.
|
||||||
|
|
||||||
|
|
||||||
|
### How `ngUpgrade` Works
|
||||||
|
|
||||||
|
Regardless of whether you choose `downgradeModule()` or `UpgradeModule`, the basic principles of
|
||||||
|
upgrading, the mental model behind hybrid apps, and how you use the {@link upgrade/static
|
||||||
|
upgrade/static} utilities remain the same. For more information, see the
|
||||||
|
[How `ngUpgrade` Works](guide/upgrade#how-ngupgrade-works) section of
|
||||||
|
[Upgrading from AngularJS](guide/upgrade).
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
The [Change Detection](guide/upgrade#change-detection) section of
|
||||||
|
[Upgrading from AngularJS](guide/upgrade) only applies to apps that use `UpgradeModule`. Though
|
||||||
|
you handle change detection differently with `downgradeModule()`, which is the focus of this
|
||||||
|
guide, reading the [Change Detection](guide/upgrade#change-detection) section provides helpful
|
||||||
|
context for what follows.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
#### Change Detection with `downgradeModule()`
|
||||||
|
|
||||||
|
As mentioned before, one of the key differences between `downgradeModule()` and `UpgradeModule` has
|
||||||
|
to do with change detection and how it is propagated between the two frameworks.
|
||||||
|
|
||||||
|
With `UpgradeModule`, the two change detection systems are tied together more tightly. Whenever
|
||||||
|
something happens in the AngularJS part of the app, change detection is automatically triggered on
|
||||||
|
the Angular part and vice versa. This is convenient as it ensures that neither framework misses an
|
||||||
|
important change. Most of the time, though, these extra change detection runs are unnecessary.
|
||||||
|
|
||||||
|
`downgradeModule()`, on the other side, avoids explicitly triggering change detection unless it
|
||||||
|
knows the other part of the app is interested in the changes. For example, if a downgraded component
|
||||||
|
defines an `@Input()`, chances are that the app needs to be aware when that value changes. Thus,
|
||||||
|
`downgradeComponent()` automatically triggers change detection on that component.
|
||||||
|
|
||||||
|
In most cases, though, the changes made locally in a particular component are of no interest to the
|
||||||
|
rest of the app. For example, if the user clicks a button that submits a form, the component usually
|
||||||
|
handles the result of this action. That being said, there _are_ cases where you want to propagate
|
||||||
|
changes to some other part of the app that may be controlled by the other framework. In such cases,
|
||||||
|
you are responsible for notifying the interested parties by manually triggering change detection.
|
||||||
|
|
||||||
|
If you want a particular piece of code to trigger change detection in the AngularJS part of the app,
|
||||||
|
you need to wrap it in
|
||||||
|
[scope.$apply()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply). Similarly, for
|
||||||
|
triggering change detection in Angular you would use {@link NgZone#run ngZone.run()}.
|
||||||
|
|
||||||
|
In many cases, a few extra change detection runs may not matter much. However, on larger or
|
||||||
|
change-detection-heavy apps they can have a noticeable impact. By giving you more fine-grained
|
||||||
|
control over the change detection propagation, `downgradeModule()` allows you to achieve better
|
||||||
|
performance for your hybrid apps.
|
||||||
|
|
||||||
|
|
||||||
|
## Using `downgradeModule()`
|
||||||
|
|
||||||
|
Both AngularJS and Angular have their own concept of modules to help organize an app into cohesive
|
||||||
|
blocks of functionality.
|
||||||
|
|
||||||
|
Their details are quite different in architecture and implementation. In AngularJS, you create a
|
||||||
|
module by specifying its name and dependencies with
|
||||||
|
[angular.module()](https://docs.angularjs.org/api/ng/function/angular.module). Then you can add
|
||||||
|
assets using its various methods. In Angular, you create a class adorned with an {@link NgModule
|
||||||
|
NgModule} decorator that describes assets in metadata.
|
||||||
|
|
||||||
|
In a hybrid app you run both frameworks at the same time. This means that you need at least one
|
||||||
|
module each from both AngularJS and Angular.
|
||||||
|
|
||||||
|
For the most part, you specify the modules in the same way you would for a regular app. Then, you
|
||||||
|
use the `upgrade/static` helpers to let the two frameworks know about assets they can use from each
|
||||||
|
other. This is known as "upgrading" and "downgrading".
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
<b>Definitions:</b>
|
||||||
|
|
||||||
|
- _Upgrading_: The act of making an AngularJS asset, such as a component or service, available to
|
||||||
|
the Angular part of the app.
|
||||||
|
- _Downgrading_: The act of making an Angular asset, such as a component or service, available to
|
||||||
|
the AngularJS part of the app.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
An important part of inter-linking dependencies is linking the two main modules together. This is
|
||||||
|
where `downgradeModule()` comes in. Use it to create an AngularJS module—one that you can use
|
||||||
|
as a dependency in your main AngularJS module—that will bootstrap your main Angular module and
|
||||||
|
kick off the Angular part of the hybrid app. In a sense, it "downgrades" an Angular module to an
|
||||||
|
AngularJS module.
|
||||||
|
|
||||||
|
There are a few things to note, though:
|
||||||
|
|
||||||
|
1. You don't pass the Angular module directly to `downgradeModule()`. All `downgradeModule()` needs
|
||||||
|
is a "recipe", for example, a factory function, to create an instance for your module.
|
||||||
|
|
||||||
|
2. The Angular module is not instantiated until the app actually needs it.
|
||||||
|
|
||||||
|
The following is an example of how you can use `downgradeModule()` to link the two modules.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Import `downgradeModule()`.
|
||||||
|
import { downgradeModule } from '@angular/upgrade/static';
|
||||||
|
|
||||||
|
// Use it to downgrade the Angular module to an AngularJS module.
|
||||||
|
const downgradedModule = downgradeModule(MainAngularModuleFactory);
|
||||||
|
|
||||||
|
// Use the downgraded module as a dependency to the main AngularJS module.
|
||||||
|
angular.module('mainAngularJsModule', [
|
||||||
|
downgradedModule
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Specifying a factory for the Angular module
|
||||||
|
|
||||||
|
As mentioned earlier, `downgradeModule()` needs to know how to instantiate the Angular module. It
|
||||||
|
needs a recipe. You define that recipe by providing a factory function that can create an instance
|
||||||
|
of the Angular module. `downgradeModule()` accepts two types of factory functions:
|
||||||
|
|
||||||
|
1. `NgModuleFactory`
|
||||||
|
2. `(extraProviders: StaticProvider[]) => Promise<NgModuleRef>`
|
||||||
|
|
||||||
|
When you pass an `NgModuleFactory`, `downgradeModule()` uses it to instantiate the module using
|
||||||
|
{@link platformBrowser platformBrowser}'s {@link PlatformRef#bootstrapModuleFactory
|
||||||
|
bootstrapModuleFactory()}, which is compatible with ahead-of-time (AOT) compilation. AOT compilation
|
||||||
|
helps make your apps load faster. For more about AOT and how to create an `NgModuleFactory`, see the
|
||||||
|
[Ahead-of-Time Compilation](guide/aot-compiler) guide.
|
||||||
|
|
||||||
|
Alternatively, you can pass a plain function, which is expected to return a promise resolving to an
|
||||||
|
{@link NgModuleRef NgModuleRef} (i.e. an instance of your Angular module). The function is called
|
||||||
|
with an array of extra {@link StaticProvider Providers} that are expected to be available on the
|
||||||
|
returned `NgModuleRef`'s {@link Injector Injector}. For example, if you are using {@link
|
||||||
|
platformBrowser platformBrowser} or {@link platformBrowserDynamic platformBrowserDynamic}, you can
|
||||||
|
pass the `extraProviders` array to them:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
||||||
|
const platformRef = platformBrowserDynamic(extraProviders);
|
||||||
|
return platformRef.bootstrapModule(MainAngularModule);
|
||||||
|
};
|
||||||
|
// or
|
||||||
|
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
||||||
|
const platformRef = platformBrowser(extraProviders);
|
||||||
|
return platformRef.bootstrapModuleFactory(MainAngularModuleFactory);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Using an `NgModuleFactory` requires less boilerplate and is a good default option as it supports AOT
|
||||||
|
out-of-the-box. Using a custom function requires slightly more code, but gives you greater
|
||||||
|
flexibility.
|
||||||
|
|
||||||
|
|
||||||
|
#### Instantiating the Angular module on-demand
|
||||||
|
|
||||||
|
Another key difference between `downgradeModule()` and `UpgradeModule` is that the latter requires
|
||||||
|
you to instantiate both the AngularJS and Angular modules up-front. This means that you have to pay
|
||||||
|
the cost of instantiating the Angular part of the app, even if you don't use any Angular assets
|
||||||
|
until later. `downgradeModule()` is again less aggressive. It will only instantiate the Angular part
|
||||||
|
when it is required for the first time; that is, as soon as it needs to create a downgraded
|
||||||
|
component.
|
||||||
|
|
||||||
|
You could go a step further and not even download the code for the Angular part of the app to the
|
||||||
|
user's browser until it is needed. This is especially useful when you use Angular on parts of the
|
||||||
|
hybrid app that are not necessary for the initial rendering or that the user doesn't reach.
|
||||||
|
|
||||||
|
|
||||||
|
A few examples are:
|
||||||
|
|
||||||
|
- You use Angular on specific routes only and you don't need it until/if a user visits such a route.
|
||||||
|
- You use Angular for features that are only visible to specific types of users; for example,
|
||||||
|
logged-in users, administrators, or VIP members. You don't need to load Angular until a user is
|
||||||
|
authenticated.
|
||||||
|
- You use Angular for a feature that is not critical for the initial rendering of the app and you
|
||||||
|
can afford a small delay in favor of better initial load performance.
|
||||||
|
|
||||||
|
|
||||||
|
### Bootstrapping with `downgradeModule()`
|
||||||
|
|
||||||
|
As you might have guessed, you don't need to change anything in the way you bootstrap your existing
|
||||||
|
AngularJS app. Unlike `UpgradeModule`—which requires some extra steps—
|
||||||
|
`downgradeModule()` is able to take care of bootstrapping the Angular module, as long as you provide
|
||||||
|
the recipe.
|
||||||
|
|
||||||
|
In order to start using any `upgrade/static` APIs, you still need to load the Angular framework as
|
||||||
|
you would in a normal Angular app. You can see how this can be done with SystemJS by following the
|
||||||
|
instructions in the [Setup](guide/setup) guide, selectively copying code from the
|
||||||
|
[QuickStart github repository](https://github.com/angular/quickstart).
|
||||||
|
|
||||||
|
You also need to install the `@angular/upgrade` package via `npm install @angular/upgrade --save`
|
||||||
|
and add a mapping for the `@angular/upgrade/static` package:
|
||||||
|
|
||||||
|
<code-example title="system.config.js">
|
||||||
|
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
Next, create an `app.module.ts` file and add the following `NgModule` class:
|
||||||
|
|
||||||
|
<code-example title="app.module.ts">
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class MainAngularModule {
|
||||||
|
// Empty placeholder method to satisfy the `Compiler`.
|
||||||
|
ngDoBootstrap() {}
|
||||||
|
}
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
This bare minimum `NgModule` imports `BrowserModule`, the module every Angular browser-based app
|
||||||
|
must have. It also defines an empty `ngDoBootstrap()` method, to prevent the {@link Compiler
|
||||||
|
Compiler} from returning errors. This is necessary because the module will not have a `bootstrap`
|
||||||
|
declaration on its `NgModule` decorator.
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
You do not add a `bootstrap` declaration to the `NgModule` decorator since AngularJS owns the root
|
||||||
|
template of the app and `ngUpgrade` bootstraps the necessary components.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
You can now link the AngularJS and Angular modules together using `downgradeModule()`.
|
||||||
|
|
||||||
|
<code-example title="app.module.ts">
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
import { downgradeModule } from '@angular/upgrade/static';
|
||||||
|
|
||||||
|
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
||||||
|
const platformRef = platformBrowserDynamic(extraProviders);
|
||||||
|
return platformRef.bootstrapModule(MainAngularModule);
|
||||||
|
};
|
||||||
|
const downgradedModule = downgradeModule(bootstrapFn);
|
||||||
|
|
||||||
|
angular.module('mainAngularJsModule', [
|
||||||
|
downgradedModule
|
||||||
|
]);
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
The existing AngularJS code works as before _and_ you are ready to start adding Angular code.
|
||||||
|
|
||||||
|
|
||||||
|
### Using Components and Injectables
|
||||||
|
|
||||||
|
The differences between `downgradeModule()` and `UpgradeModule` end here. The rest of the
|
||||||
|
`upgrade/static` APIs and concepts work in the exact same way for both types of hybrid apps.
|
||||||
|
See [Upgrading from AngularJS](guide/upgrade) to learn about:
|
||||||
|
|
||||||
|
- [Using Angular Components from AngularJS Code](guide/upgrade#using-angular-components-from-angularjs-code).
|
||||||
|
- [Using AngularJS Component Directives from Angular Code](guide/upgrade#using-angularjs-component-directives-from-angular-code).
|
||||||
|
- [Projecting AngularJS Content into Angular Components](guide/upgrade#projecting-angularjs-content-into-angular-components).
|
||||||
|
- [Transcluding Angular Content into AngularJS Component Directives](guide/upgrade#transcluding-angular-content-into-angularjs-component-directives).
|
||||||
|
- [Making AngularJS Dependencies Injectable to Angular](guide/upgrade#making-angularjs-dependencies-injectable-to-angular).
|
||||||
|
- [Making Angular Dependencies Injectable to AngularJS](guide/upgrade#making-angular-dependencies-injectable-to-angularjs).
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
While it is possible to downgrade injectables, downgraded injectables will not be available until
|
||||||
|
the Angular module is instantiated. In order to be safe, you need to ensure that the downgraded
|
||||||
|
injectables are not used anywhere _outside_ the part of the app that is controlled by Angular.
|
||||||
|
|
||||||
|
For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
|
||||||
|
from Angular components, but it is _not OK_ to use it in an AngularJS component that may be used
|
||||||
|
independently of Angular.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
## Using ahead-of-time compilation with hybrid apps
|
||||||
|
|
||||||
|
You can take advantage of ahead-of-time (AOT) compilation in hybrid apps just like in any other
|
||||||
|
Angular app. The setup for a hybrid app is mostly the same as described in the
|
||||||
|
[Ahead-of-Time Compilation](guide/aot-compiler) guide save for differences in `index.html` and
|
||||||
|
`main-aot.ts`.
|
||||||
|
|
||||||
|
AOT needs to load any AngularJS files that are in the `<script>` tags in the AngularJS `index.html`.
|
||||||
|
An easy way to copy them is to add each to the `copy-dist-files.js`file.
|
||||||
|
|
||||||
|
You also need to pass the generated `MainAngularModuleFactory` to `downgradeModule()` instead of the
|
||||||
|
custom bootstrap function:
|
||||||
|
|
||||||
|
<code-example title="app/main-aot.ts">
|
||||||
|
import { downgradeModule } from '@angular/upgrade/static';
|
||||||
|
import { MainAngularModuleNgFactory } from '../aot/app/app.module.ngfactory';
|
||||||
|
|
||||||
|
const downgradedModule = downgradeModule(MainAngularModuleNgFactory);
|
||||||
|
|
||||||
|
angular.module('mainAngularJsModule', [
|
||||||
|
downgradedModule
|
||||||
|
]);
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
And that is all you need to do to get the full benefit of AOT for hybrid Angular apps.
|
||||||
|
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
This page covered how to use the {@link upgrade/static upgrade/static} package to incrementally
|
||||||
|
upgrade existing AngularJS apps at your own pace and without impeding further development of the app
|
||||||
|
for the duration of the upgrade process.
|
||||||
|
|
||||||
|
Specifically, this guide showed how you can achieve better performance and greater flexibility in
|
||||||
|
your hybrid apps by using {@link downgradeModule downgradeModule()} instead of {@link UpgradeModule
|
||||||
|
UpgradeModule}.
|
||||||
|
|
||||||
|
To summarize, the key differentiating factors of `downgradeModule()` are:
|
||||||
|
|
||||||
|
1. It allows instantiating or even loading the Angular part lazily, which improves the initial
|
||||||
|
loading time. In some cases this may waive the cost of running a second framework altogether.
|
||||||
|
2. It improves performance by avoiding unnecessary change detection runs while giving the developer
|
||||||
|
greater ability to customize.
|
||||||
|
3. It does not require you to change how you bootstrap your AngularJS app.
|
||||||
|
|
||||||
|
Using `downgradeModule()` is a good option for hybrid apps when you want to keep the AngularJS and
|
||||||
|
Angular parts less coupled. You can still mix and match components and services from both
|
||||||
|
frameworks, but you might need to manually propagate change detection. In return,
|
||||||
|
`downgradeModule()` offers more control and better performance.
|
|
@ -515,8 +515,8 @@
|
||||||
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
|
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "guide/upgrade-lite",
|
"url": "guide/upgrade-performance",
|
||||||
"title": "Lazy-loading Angular into AngularJS",
|
"title": "Upgrading for Performance",
|
||||||
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
|
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,7 +48,7 @@ import {NgAdapterInjector} from './util';
|
||||||
* {@example upgrade/static/ts/lite/module.ts region="basic-how-to"}
|
* {@example upgrade/static/ts/lite/module.ts region="basic-how-to"}
|
||||||
*
|
*
|
||||||
* For more details on how to use `downgradeModule()` see
|
* For more details on how to use `downgradeModule()` see
|
||||||
* [Upgrading for Performance](guide/upgrade-lite).
|
* [Upgrading for Performance](guide/upgrade-performance).
|
||||||
*
|
*
|
||||||
* @usageNotes
|
* @usageNotes
|
||||||
*
|
*
|
||||||
|
@ -86,7 +86,7 @@ import {NgAdapterInjector} from './util';
|
||||||
* notify each framework as needed.
|
* notify each framework as needed.
|
||||||
*
|
*
|
||||||
* For a more detailed discussion of the differences and their implications, see
|
* For a more detailed discussion of the differences and their implications, see
|
||||||
* [Upgrading for Performance](guide/upgrade-lite).
|
* [Upgrading for Performance](guide/upgrade-performance).
|
||||||
*
|
*
|
||||||
* <div class="alert is-helpful">
|
* <div class="alert is-helpful">
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue