Running AngularJS 1.6 in Angular 5 (side by side)
At Spatial Vision, We’ve got a number of AngularJS (1.2.x ~ 1.6.x) apps in production which are built around 2012 ~ 2015. Their main tech stack:
- Angular 1.x
- Bootstrap 3.x
- LocalStorage (ngstorage)
- Font Awsome
- Moment JS (Date/time)
- Grunt for build
- Docker for build process
Many apps are stable and there is no reason to migrate/upgrade (commercially) for Angular 5 (6 is coming soon). However some apps have on-going enhancement works and require new libraries for new features.
When you need a new library or external capabilities, most libraries (Angular wrapper etc.) are now only targeting Angular 2+.
Data Farming Mapping Website is a good example that they now want to add a shopping cart capability including eWay integration. While it is entirely possible to stay in 1.6, there is a risk of adapting an external capabilities that are no longer maintained.
ngUpgrade: Run AngularJS and Angular side by side
This topic is not new and a lot of materials have been published over the last couple of years (Since Angular 2). We learned everything through a number of blogs and official documentation, which are a kind of outdated as Angular is evolving so quickly even since early 2017 and things are slightly different as of March 2018.
It is still a good idea to go through the history of the migration process as well as reading the official documentation:
- One of the recent blog about the topic: https://medium.com/code-divoire/a-story-of-ngupgrade-bringing-an-angularjs-application-from-1-6-to-angular-4-84eae4434010
- Insight of ngUpgrade, very helpful to understand architectural part of ngUpgrade: https://blog.nrwl.io/ngupgrade-in-depth-436a52298a00
- Somehow outdated but it provides the mechanics in detail: https://angular.io/guide/upgrade
- The only working implementation found (via the 1st blog): https://github.com/manfredsteyer/ngUpgrade-without-preparation
Case Study: Data Farming Mapping Website
AngularJS app overview
We’ve selected the Data Farming app as a migration candidate as we’re about to implement some new features including an e-commerce capability using eWay and a custom shopping cart for downloadable shape files.
The app was originally created in order to demonstrate the API capability, however it was quickly evolved to an app used by farmers and consultants directly. Technically speaking, it has the following characteristics:
- AngularJS 1.6 + Angular Material, Grunt base and a few libraries including Auth0, OpenLayer 3, jsPDF and introJS
- The app is relatively small (Login; List Farms; Draw paddocks in map; Upload KML/Shape files; View NDVI images for paddocs and print PDF)
- Building a new feature which requires a new library (eWay) and new UX (shopping cart, purchase history etc.)
- The existing code base needs to be reviewed prior to extending as it was built for a simple prototype
Migration step summary
The following are the steps we took after a lot of readings. There are different migration strategies available in the official documentation, however our preference is running side by side with Angular 5 being the main application.
- Create Angular 5 application using Angular CLI (1.7.3)
- Copy an existing Angular JS app (should be 1.6.x and refactored so the structure of the code is feature based and each file has a single impl)
- Migrate JS dependencies (Switch npm or copy bower_components)
- Fix template paths in both JS and HTML
- Migrate scss files and fix import paths
- Fix API proxy path from Grunt to Angular CLI
- Fix minor CSS issues caused by an itroduction of view-container and view-frame
- Regression test (It seems all working at this stage, have a good test by QA)
- Add Angular Routing and introduce a new page
- Switch between AngularJS page and Angular page
Create Angular 5 application using Angular CLI (1.7.3)
Firstly, create an Angular 5 app using Angular CLI. (Used Node 8.10.0 LTS)
npm install -g @angular/cli
ng new ng52
Copy an existing Angular JS app into src/ng1
The app is s Grunt based Angular JS 1.6 app using Bower for web dependencies, NodeJS for the build process and SCSS for css processing.
We firstly copied the app directory into src of Angular 5 app:
Migrate JS dependencies (copy bower_components)
Most of our JS dependencies are from bower, which essentially copies GitHub repos into bower_components. We also have some dependencies fetched via CDN as well as embedded JS library.
Whilst it is possible to switch all dependencies into npm, our approach is to simply copy the required dependencies as a part of embedded scripts in Angular 5.
This decision is based on the time we want to spend as well as these dependencies will not be updated during the migration. (Of course all of these can be migrated over a period of time when needed).
When all dependencies are copied, simply list them in scripts of .angular.cli.json, in the same order as the original index.html. (followed by the application code)
Bootstrap AngularJS in Angular
At this point, Angular JS can be bootstrapped and show a page as all JS runtime is available. First install upgrade module and imports the module:
npm install @angular/upgrade --save
Then bootstrap AngularJS app in app.component.ts
Lastly create a template for AngularJS app in app.component.html
Run the application by ng serve and we see AngularJS app running with a few errors including no style and 404 errors for html templates.
Fix template paths in both JS and HTML
At this point, AngularJS app is served in src/ng1/app directory and breaks the paths for partial htmls:
To fix 404 errors, add ng1 in .angular-cli.json
Paths in html (ng-include) and js (routing, directives etc.)
After fixing the templates, we now see the page without style
Migrate scss files and fix import paths
AngularJS app uses scss, which is supported by Angular CLI. You can specify it when you create an app (or update in .angular-cli.json):
Once scss is setup, then specify all scss files in styles of .angular-cli.json. We took this approach instead of fixing @import of scss so these dependencies are visible in .angular-cli.json and we can reuse them in Angular 5 app.
Then fix variable references and path errors
Fix API proxy path from Grunt to Angular CLI
AngularJS app uses Grunt proxy, which we can simply replace with proxy.conf.json:
Start app with
"start": "ng serve --proxy-config proxy.conf.json",
Fix minor CSS issues by view-container and view-frame
The example GitHub repo has the following app.component.html. AngularJS app is rendered in ng-view and Angular 5 app is rendered in router-outlet.
<div ng-view class="view-frame"></div>
<!-- router-outlet></router-outlet -->
AngularJS app does not expect this extra divs and we have to fix css accordingly.
AngularJS app seems all working at this stage, so we pass it to QA and make sure every part of app is working.
Add Angular Routing and introduce a new page
AngularJS app is now successfully running in Angular 5 app, it is time to introduce a brand new page in Angular 5. We can just run a few Angular CLI command and create a component and a module.
ng g component home
Switch between AngularJS page and Angular 5 page
The last part is to switch the routing between AngularJS and Angular 5. We took the following implementation from the example GitHub repo:
CustomHandlingStrategy checks the URL and decide whether the URL passed should be handled by Angular 5, otherwise simply forward to the AngularJS app.
We’ve successfully run 2 apps side by side!
We’re now ready to start a new feature in Angular 5 including how to share the common resources such as header/footer, Local storage etc.
We’ll share our findings in the next post.