Hello, coders. Although Angular that is written with the help of JavaScript is still very popular among developers, the new versions of Angular that are built on TypeScript are gaining more and more popularity every day. In this article, I am going to light up the question about how to accomplish the Angular migration with the help of ngUpgrade.
What is ngUpgrade and how does it work?
As part of Angular 2, ngUpgrade enables parallel operation of the upcoming version of the framework and its predecessors. Therefore, it is suitable as a basis for a creeping migration.
Since the announcement of Angular 2, development teams have been asking how their AngularJS applications can be prepared for an upgrade. The ngUpgrade that ships with Angular 2 is one answer. It allows AngularJS and 2 to run in parallel and thus forms the basis for a step-by-step migration. With the tool, developers have the option of writing new application parts with Angular 2 without having to migrate the previous components immediately.
This article illustrates the process using an example. The associated source code can be found on GitHub. It uses the TypeScript language for AngularJS as well as for Angular 2 and compiles the constructs created with it for execution in the browser according to ECMAScript 5.
The following figure outlines the structure of the application presented.
Accordingly, it is an AngularJS application, which is also a requirement for the use of ngUpgrade. Within the application, however, components (directives) and services that were written with both AngularJS and Angular 2 are used. The framework version used can be found in the illustration of the components at the top left.
In order to use Angular 2 constructs in AngularJS, they must be downgraded. This means that developers wrap them with a wrapper provided by ngUpgrade so that they look like an AngularJS structure to the outside world. In the case under consideration, this happens with FlugCard, FlugService and the Passenger Search component. NgUpgrade also provides AngularJS constructs with a wrapper for use within Angular 2 on request. In this case, there is talk of an upgrade. In the example under consideration, this is necessary for the PassengerCard and PassengerService.
How to set up the work and bootstrap the process?
Both the bundles for AngularJS and those for Angular 2 including ngUpgrade are included in the example via script references. The application is written in TypeScript. The ECMAScript-5 files compiled from the sources are loaded by the application via the module loader System.js:
System.import (‘app / app’). Catch (console.error.bind (this));
Since the use of the ECMAScript 2015 modular system in TypeScript means that each file announces its dependencies via import instructions, the application only has to request the loading of the app / app.js file. As is usual with AngularJS applications, it registers the individual constructs, including controllers and services. In addition, it takes care of the application bootstrapping. However, angular.bootstrap is not used for this, but the method of the same name from the so-called UpgradeAdapter by ngUpgrade. It is the linchpin for the desired parallel operation. However, ngUpgrade does not offer a counterpart to bootstrapping via the ng-app attribute. Since several places in the application need the same instance of the upgrade adapter, it is advisable to instantiate it in a separate file:
// upgrade-adapter.ts
import {UpgradeAdapter} from ‘angular2 / upgrade’;
export const upgradeAdapter = new UpgradeAdapter ();
The file app.ts can then be imported by the UpgradeAdapter and used to bootstrap the application:
// app.ts
import {upgradeAdapter} from ‘./upgrade-adapter’;
[…]
var app = angular.module (‘app’, [‘ui.router’]);
[…]
upgradeAdapter.bootstrap (document.body, [‘app’]);
What are up- and downgrades?
The downgradeNg2Component method of the upgrade adapter is used to downgrade the aforementioned Angular 2 component Passenger Search. App.directive registers the returned wrapper as a directive:
// app.ts
app.directive (‘passenger search’, ↵
<any> upgradeAdapter.downgradeNg2Component (Passenger Search));
So that the UI router can activate it, its configuration receives a state with a template that is based on it:
$ stateProvider
.state (‘passenger-search’, {
url: ‘/ passenger-search’,
template: ‘<passier-suchen> </passagier-suchen>’
});
Another example of using an Angular 2 component can be found in the next two source code excerpts. This is the flight card shown in the overview graphic. As with the FlugSuche component, app.ts takes care of the downgrade and registration:
// app.ts
app.directive (‘flugCard ‘, ↵
<any> upgradeAdapter.downgradeNg2Component (FlugCard));
The use of such a directive in an Angular 1 template is then as follows:
// flight-search.html (ng1)
<div class = “col-sm-4” style = “padding: 10px;” ng-repeat = “f in ↵
FlugSuchen.flege “>
<flight card
[item] = “f”
[selected-item] = “flightSuchen.selectedFlug”
(selected-item-change) = “flightSuchen.select ($ event)”>
</flug-card>
</div>
Please, note that the notation known from Angular 2 is used to set up the data binding. Square brackets therefore stand for a one-way binding, while round brackets stand for an event binding. A combination of both corresponds to a two-way binding. The example under consideration illustrates this using selected-item and selected-item-change.
Angular 2 service in Angular 1
The use of Angular 2 services is similar to the use of corresponding components. However, providers for the service and its dependencies must also be registered with the UpgradeAdapter:
// app.ts
upgradeAdapter.addProvider (FlugService);
upgradeAdapter.addProvider (HTTP_PROVIDERS);
Since FlugService uses the HTTP service provided by Angular 2, the example registers the HTTP_PROVIDERS array, which contains the required providers. In addition, the downgradeNg2Provider method takes care of downgrading the service. Then app.factory registers the wrapper returned by it:
app.factory (‘flugService ‘, upgradeAdapter.downgradeNg2Provider (FlugService));
In order to be able to use existing AngularJS constructs in Angular 2 components or services, they must be updated with upgradeNg1Provider:
// app.ts
app.service (‘passenger service’, passenger service);
upgradeAdapter.upgradeNg1Provider (‘passengerService’);
Afterwards, Angular 2 components and services can request the service via dependency injection. Since optional type specifications do not play a special role for AngularJS 1.x, the name of the desired service must be specified using the Inject decorator:
// passenger-search.ts (ng2)
@Component ({[…]})
export class passenger search {
constructor (
@Inject (‘passengerService’) private passengerService: ↵
Passenger Service,
private shopping cart service: shopping cart service) {}
[…]
}
- Directives
If developers want to reuse an AngularJS directive in a new Angular component, it must also be adapted with upgradeNg1Component. The method takes the name under which the directive is registered and returns a wrapper for Angular 2. The component then only has to add it to its list of directives.
// passenger-search.ts (ng2)
@Component ({
selector: ‘passenger search’,
templateUrl: ‘app / passenger-search / passenger-search.html’,
directives: [upgradeAdapter.upgradeNg1Component (‘passengerCard’)]
})
export class passenger search {
[…]
}
The template of the component can reference the directive. To do this, it uses an element with the name under which the directive was originally registered.
// passenger-search.html (ng2)
[…]
<div * ngFor = “# p of passengers” class = “col-sm-4” style = “padding: 10px;”>
<passenger card
[item] = ‘p’
[selectedItem] = “selectedPassagier”
(selectedItemChange) = “select ($ event)”>
</passier-card>
</div>
[…]
In this case, too, the notation known from Angular 2 is used to set up the bindings.
In order for an AngularJS directive to be used in Angular 2, it must be based on some best practices. For example, it is advisable to use the relatively new properties controllerAs and bindToController. In addition, the link and compile functions should be avoided, especially since Angular 2 does not offer any equivalent:
// passenger-card-factory.ts (ng1)
export class PassengerCardFactory {
static create (): ng.IDirective {
return {
templateUrl: ‘app / passenger-card / passenger-card.html’,
controllerAs: ‘vm’,
scope: {
item: ‘=’,
selectedItem: ‘=’
},
bindToController: true,
controller: function () {
this.item = null;
this.select = () => {
this.selectedItem = this.item;
}
app.service (‘passengerCard’ PassengerCardFactory.create);
module.component from Angular 1.5
With the convenience function module.component, Angular offers an alternative to module.directive, which enforces the best practices described here.
Conclusion
So, ngUpgrade enables the parallel operation of AngularJS and Angular 2 and thus a creeping migration. While Angular 2 can already be used for new areas, existing constructs cannot be transferred immediately.
The fact that ngUpgrade comes directly with Angular 2 also has a strong symbolic character. It shows that the Angular team has not forgotten its most important success factor. This means its strong community, which despite the new beginning of Angular 2 still has to maintain AngularJS apps.