Upgrading with Web Components: From AngularJS to Angular

Upgrading from AngularJS to Angular can be challenging. Framework-independent Web Components can help here. Fortunately, Angular now allows exposing Web Components and beginning with AngularJS 1.7.3 you can easily consume them.

To be more precise, we should use the word Custom Elements instead of Web Components as Web Components is an umbrella term for several standards. According to https://custom-elements-everywhere.com/, they really work well with AngularJS:

AngularJS seamlessly works together w/ Custom Elements

In this post, I show how to use this for migrating an AngularJS solution step by step to Angular. For this, I write new application parts with Angular and provide them as Custom Elements which are loaded into the AngularJS context:

Sample for upgrading

You can tell by the logos and the dashed lines where the AngularJS and where the Angular parts are.

The source code can be found in my GitHub account.

Project Structure

The advantage of this approach is, that one doesn't need to intermingle AngularJS and Angular code. Instead, both parts of the application are written separately and the integration happens at runtime when a bundle with Custom elements is loaded.

Hence, when you look at the folder structure of my example, you will find an own folder for each of those parts:

Folder Structure

Angular Part

The Angular folder contains this component displaying a flight as shown above:

@Component({ templateUrl: './flight-card.component.html' }) export class FlightCardComponent implements OnChanges { @Input() item: any; @Input() selected: boolean; @Output() selectedChange = new EventEmitter<boolean>(); deactivate(): void { // this.selected = false; this.selectedChange.emit(false); } activate(): void { // this.selected = true; this.selectedChange.emit(true); } }

This component is exposed as an external web component in its module:

import { createCustomElement } from '@angular/elements'; [...] @NgModule({ imports: [ BrowserModule ], declarations: [ FlightCardComponent ], bootstrap: [], entryComponents: [ FlightCardComponent ] }) export class AppModule { constructor(private injector: Injector) { } ngDoBootstrap() { const flightCardCE = createCustomElement(FlightCardComponent, { injector: this.injector }); customElements.define('flight-card', flightCardCE); } }

As you see here, the component is not only declared but also put into the module's entryComponents section. This is because Angular Element instantiates it dynamically at runtime.

In ngDoBootstrap which is called during bootstrapping the Angular application, createCustomElement wraps it as a Custom Element and customElements.define registers it with the browser.

As you see in this example, when wrapping the component the current injector is passed. Hence, the component can make use of dependency injection. As Angular Element's father, Rob Wormald, pointed out in his talk at ngConf 2018, this also allows different web components provided by the same application to communicate with each other using services.

To make sure it works with every browser, I'm using the the polyfill @webcomponents/custom-elements.

Further information about using this polyfill, you can find in my blog article series regarding Angular Elements.

To provide the AngularJS application with the bundles, the example uses a simple npm script copying it over using the cpr nodejs package:

"build": "npm run bundle:ce && npm run copy:ce",
"copy:ce": "cpr dist/demo ../angularjs/angular-ce -d",
"bundle:ce": "ng build --prod --output-hashing none"

AngularJS part

The AngularJS application references both, the AngularJS bundle as well as the bundles from the Angular part:

<!-- Bundles for Angular part --> <script type="text/javascript" src="angular-ce/runtime.js"></script> <script type="text/javascript" src="angular-ce/polyfills.js"></script> <script type="text/javascript" src="angular-ce/scripts.js"></script> <script type="text/javascript" src="angular-ce/main.js"></script> <!-- Bundles for AngularJS part --> <script src="main.js"></script>

As it also references the polyfills bundle and the scripts bundle from the Angular part, we get the mentioned polyfills too.

After this, you can use the Custom Element within the AngularJS templates:

<div ng-repeat="b in $ctrl.bookings"> <flight-card ng-prop-item="b" ng-prop-selected="$ctrl.selection[b.id]" ng-on-selected_change="$ctrl.change(b, $event)"></flight-card> </div>

Please have a look at the new directives ng-prop-* and ng-on-* which have been introduced with AngularJS 1.7.3. They allow to bind any property and any event of an (custom) element. Please also note, that the name selectedChange becomes selected_change here which is due to a convention of those directives.

Exposing AngularJS components as Custom Elements

Ok, so far we've seen how to expose an Angular Element as an Custom Element which can be used in an AngularJS application. But what's with the other way round? Is is also possible to wrap an AngularJS component as an Custom Element?

Well, while there is not an official solution for this, no one can prevent you from manually wrapping AngularJS stuff. I'm using this technique in my showcase for combinding several technologies by means of Web Components:

Micro App

As described in my article about this, this example consists of an Angular application -- the shell -- which loads Micro Apps provided as Web Components on demand. One of those Micro Apps is the AngularJS based booking app shown above.

The full source code for this micro apps example can be found here.

Conclusion and Evaluation

Thanks to the Angular(JS) team's great support for Custom Elements, using this modern technique for migrating from AngularJS to Angular is straight forward: Just write the Angular parts in an new Angular solution and integrate them into the existing AngularJS counterpart by leveraging Custom Elements. Due to the newly introduced ng-prop-* and ng-on-* calling them within AngularJS is just a piece of cake.

After every building block of the AngularJS part has been replaced by such a Custom Element, you can fully switch to Angular.

One advantage over using ngUpgrade is that the Angular and the AngularJS parts are not intermingled which reduces complexity. However, ngUpgrade on the other side is more powerful. It not only allows using Angular components within AngularJS applications but also using AngularJS components within Angular applications without the need to wrap them by hand. Also, it allows mutual access to services on both sides.

 

 
Hier können Sie eine Anfrage für eine unverbindliche Schulung ode Beratung bzw. einen Workshop erstellen.
 
Unverbindliche Anfrage
 
 

Schulungen

Angular Schulung: Strukturierte Einführung

Lernen Sie in dieser interaktiven Schulung anhand einer Beispielanwendung den Einsatz von Angular für Ihre erfolgreichen Projekte kennen. Sie durchdringen die Hintergründe und bauen von der ersten Minute an auf Best Practices auf.

Details

Advanced Angular: Enterprise-Anwendungen und Architektur

In dieser Schulung erfahren Sie alles für die Entwicklung großer Anwendungen mit Angular: Mono-Repos, Micro-Apps, State Management, Performance und mehr

Details

Angular: Strukturierte Einführung

Seit der Ankündigung von Angular (2+) fragen sich Entwicklungs-Teams, welche Migrationspfade für AngularJS-1.x-Anwendungen vorliegen werden. Das im Lieferumfang von Angular enthaltene ngUpgrade bietet eine Antwort darauf. Es erlaubt einen Parallelbetrieb von AngularJS 1.x und Angular (2+) und stellt somit die Grundlage für eine schrittweise Migration dar.

Details

Progressive Web-Apps mit Angular

Progressive Web Apps bieten den Komfort nativer Anwendungen, indem sie auf moderne Browser APIs, wie Service Worker, setzen. Sie sind installierbar sowie offlinefähig und nutzen Hintergrundprozesse für Datensynchronisation und Push-Notifications. Diese Schulung zeigt anhand eines durchgehenden Beispiels was sich genau hinter diesem neuen Konzept verbirgt, wie solche Anwendungen mit Angular entwickelt werden und wie Sie in Ihren Projekten von den dahinterstehenden Ideen profitieren.

Details

Reaktive Architekturen mit Angular und Redux

Dieses interaktive Seminar vermittelt, wie Sie reaktive Anwendungen mit Angular entwickeln können.

Details

TypeScript

TypeScript gibt Ihnen alle Möglichkeiten der neuen JavaScript-Standards und zusätzlich ein statisches Typsystem, das dabei hilft, Fehler möglichst früh zu erkennen. Außerdem ist TypeScript die Grundlage für Angular. In diesem interaktiven Seminar lernen Sie diese mächtige Sprache anhand einer Fallstudie kennen.

Details

Weitere Schulungen ...