A story of ngUpgrade: Bringing an AngularJS application from 1.6 to Angular 4

After reviewing an existing AngularJS application that was to be used as the basis for an enterprise dashboard application, i quickly determined that the best way forward would be to move the existing application to Angular before implementing the required new features in the more modern versions of the framework.

AngularJS in Typescript

Understanding ngUpgrade

Before embarking on this task, I first had to understand the nature of what I would be doing, I highly recommend the following material as preparatory reading:

These links should help you understand the nature of the upcoming tasks. Given my own circumstances I had to go about it in the following way:

  1. Upgrade the existing application’s AngularJS version to 1.6 from 1.3 and get it running in a Angular CLI environment.
  2. Set up the routing handling between Angular and AngularJS
  3. Componentize the 1.6 application using John Papa’s style guide
  4. Upgrade the componentized application to use Typescript
  5. Upgrade the Typescript components to Angular components

Angular CLI and the Upgrade process

I scaffolded a new project using the CLI, and installed the Angular UpgradeModule:

npm install @angular/upgrade --save

Next i manually imported all the of the existing AngularJS files and assets from their old location. It took some work to get the application up since we’d basically be giving up on the old build system and manually loading the AngularJS files. In my case give the relative small number of files it was worth dealing with the loss of minification until the upgrade process was completed.

Loading AngularJS *.js files in Angular-CLI

Due to my set up as previously mentioned where I had a relatively small number of files to load for the existing AngularJS application. I used regular script tags to load them in the index.html file of the cli application. My goal was to then used the scripts and styles entries in the .angular-cli.jsonto load them once every issue would have been resolved with bootstrapping the AngularJS application. For existing apps that use Gulp or Grunt as a build system, I think you would have to eject your webpack configuration to include the gulp build files in your existing cli configuration but don’t take my word for it. This was temporary in any case as these .js files would eventually be updated to Typescript .tsfiles during the upgrade process.

Setting up routing between AngularJS and Angular components

Next I needed to make sure that i could access the existing AngularJS application from my Angular setup. This took the most time as it meant familiarizing myself with the whole upgrade process and how it made both frameworks interact with each other. This implementation here was really useful in showing how to upgrade an existing application, in this case the vanilla Phonecat AngularJS implementation and its based on the official upgrade tutorial. Upgrading my app from v1.3 to 1.6 i ran into a routing error in AngularJS that took me a while to figure out and fix so if you ever run into weird url encoding issues while upgrading, definitely check out the related article here.

Upgrading from ES5 to Typescript

Once the application was working with the routing set up properly, the next step was to upgrade to Typescript. There is enough documentation on how to proceed if you know where to look for it for the different Angular elements (controllers, filters, directives, services) and how they can best be represented in Typescript and for filters and directives, there are a couple of ways you can go about upgrading them to Typescript.

Here are some additional tips form my experience:

  • Make sure to move all your library dependencies to NPM. This will lead to some errors, and in particular, loading AngularJS , something along the lines of :
AppComponent_Host.html:1 ERROR Error: AngularJS v1.x is not loaded!
    at Object.noNg (static.es5.js:15)
    at module$1 (static.es5.js:55)
    at UpgradeModule.webpackJsonp.../../../upgrade/@angular/upgrade/static.es5.js.UpgradeModule.bootstrap (static.es5.js:1249)
    at AppComponent.webpackJsonp.../../../../../src/app/app.component.ts.AppComponent.ngOnInit (app.component.ts:17)
    at checkAndUpdateDirectiveInline (core.es5.js:10848)
    at checkAndUpdateNodeInline (core.es5.js:12349)
    at checkAndUpdateNode (core.es5.js:12288)
    at debugCheckAndUpdateNode (core.es5.js:13149)
    at debugCheckDirectivesFn (core.es5.js:13090)
    at Object.eval [as updateDirectives] (AppComponent_Host.html:1)

you basically have to make sure that AngularJS is imported before loading your Angular app’s main.js.

  • This might be due to my ignorance of the angular-cli inner workings or the way Typescript module loading works but i ran into this issue where not all my Typescript files where not being compiled and included in the final bundle. I wanted to keep things modular by keeping each module defined first, and then import the module into for example a service file in which I would declare the service, then .service() attach it to the corresponding Angular module. This approach resulted in services, filters and other AngularJS components not being included in the final bundle file by Webpack. The correct approach is to import all the necessary components in the module definition file and then attach them to their particular component, a la:
import { CoreModule } from './../core/core.module';
import { ActionPopoverComponent } from './action-popover.component';
import { ActionPopoverService } from './action-popover.service';
import { ActionPopoverFilter } from './action-popover.filter';
import * as angular from 'angular';
import { NgModule } from '@angular/core';
export const CommonModule: angular.IModule = angular.module(
'actionApp.common',
[CoreModule.name]
);
CommonModule.component('actionPopover', ActionPopoverComponent);
CommonModule.service('actionPopoverService', ActionPopoverService);
CommonModule.service('actionPopoverFilter', ActionPopoverFilter);
@NgModule({})
export class CommonNgModule {}

There are also some gotchas with for example finding the best way to represent a ES5 Angular as either a Typescript function or a Typescript class, obviously representing as a class makes it easier to then transform into an Angular pipe but it introduces complexities when your filter relies on some injected services.

Upgrade the Typescript components to Angular components

Last but not least once your app is once again working all in Typescript, you can take the approach of now upgrading all AngularJS Typescript components to their Angular versions. This is not an exact science and will undoubtedly involves rewrites and refactors, especially if you were using 3rd party libraries that you will also need to upgrade to their Angular equivalent if available or alternatives if not. This is also a good opportunity to review your code and maybe improve upon it time permitting. There are better guides on this specific part out there on the internet but it was one the most fun part as it really involves bettering your knowledge of Angular and AngularJS. There are some

Summary

The process of upgrading an application from AngularJS can be daunting because it involves having a good deal of comfort with 3 domain knowledges: AngularJS, Angular and Typescript. For an individual or team coming in with good AngularJS knowledge, I would suggest getting comfortable with Angular and Typescript before attempting the movement, maybe by implementing a brand new feature in Angular to get a feel for how everything fits before trying to translate AngularJS paradigms into this new stack. The Angular team did a good job though to provide tools to make this process less daunting then it is.

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s