Running AngularJS 1.6 in Angular 5 (side by side) Part 2: Build new features in Angular 5

Side by side

Building new features in Angular 5

Today, we’d like to share some lessons learned on running Angular JS 1.6 in Angular 5.

In Part 1, we shared how we migrated Data Farming (Angular JS 1.6) app to Angular 5 + Angular JS. Our focus was to run Angular JS app in Angular 5 app.

In Part 2, we’d like to talk about:

  • How to use Angular JS service in Angular 5?
  • How to share the map state so the map does not flicker when moving between Angular JS and Angular 5 routing
  • How to share Authentication mechanism (Auth0 + JWT)?

What about Angular 6?

Angular 6 has been released in the mean time. We’re now using Angular 6 for a new project but we haven’t updated this DataFarming project yet.

Angular 6 offers a lot of new features and great improvement in CLI centric development (Schematics) and supposedly easier to migrate than ever (What’s new in Angular (Google I/O ‘18).

However one of the big changes is the configuration json file has been changed from ‘.angular.cli.json’ to ‘angular.json’ and although they are similar, it is not compatible in a way you can move from 5 to 6 without changing it.

For for now, we’ll stick with Angular 5.

How to use AngularJS 1.6 service in Angular 5?

The main feature of DataFarming app is a map view (using OpenLayers 4), which lets you draw your farm paddocks and get the NDVI imagery to see the growth of your crop over a season (using Sentinel Satellite image).

NDVI imagery of a paddock

The new feature we’re adding is to further classify the NDVI image by the NDVI values and produce a shape file so the user (farmer) can use it for a variable rate fertiliser. (the following machine takes the shapefile with a zoned variable rate and feed certain areas of paddocks accordingly).

Variable Rate Application

Our aim is to share the OpenLayers map implementation between AngurlarJS and Angular 5, so we can build a new and identical map view in Angular 5 while keeping the existing features (draw paddocks, view NDVI images etc.) untouched.

AngularJS Map Service

The following shows the map view feature components in AngularJS. We use a feature based code organisation so everything related to the map view sits within ng1/app/features/map directory.

AngularJS map feature

Maps.src.js

The main service is called ‘Maps’, using AngularJS service factory. (It uses the injection via a string name array, so we can minify this service. I’ll come back to this topic later).

Maps.src.js

Maps provides a set of public APIs we’d like to re-use in Angular 5.

return {
defaults: mapDefaults,
create: _create,
loadFarm: _loadFarm,
find: _find,
clearLayers: _clearLayers,
getFeatureList: _getUploadedFeatureList,
setFeatureList: _setUploadedFeatureList,
shouldResetState: _shouldResetState,
setMapState: _setMapNewState,
shouldReload: _shouldReload,
shouldClearLayer: _shouldClearLayer,
getSelectedPaddock: _getSelectedPaddock,
canvas: function () {
return mapCanvas;
},
show: _show,
hide: _hide
};

Expose Maps AngularJs service in Angular 5

We firstly create a map module in Angular 5.

Angular 5 map module

and then inject AngularJs Maps in the Map module.

The following shows the map module injecting Maps as a provider.

map.module.ts

We then create Angular 5 Maps in order to provide Angular JS Maps API as a delegate. (see show() function, which calls Angular JS Maps.show).

map.service.ts

How share AngularJS Component with Angular?

Routing and Map view lifecycle

Both AngularJS and Angular use a routing mechanism to decide which view to show when a specific path is accessed in your application.

Our map view is the busiest part of application and we have multiple AngularJS controllers, controlling different parts of UI.

In AngularJS each route is usually controlled by it’s own specific controller and accessing the route instantiates that controller.

Similar concept on Angular side exist. You have Angular components as part of your main module or feature modules that are accessed by different routes which get instantiated when a specific route is accessed.

In the previous application (AngularJs version), we had a specific routing path for accessing map /#/map?id=farmId

Accessing this route instantiates map controller and bootstrap whole web mapping experience.

So map lifecycle (eg: creation, destroying) is managed through accessing the route and moving away from this route respectively.

Accessing this route also initiates other controllers. Map template (.html) has multiple <ng-include> which each has its own controller and uses the controller as syntax.

Map Template initiating SidePanelCtrl

The highlighted `<ng-include>` for SidePanelCtrl is one of the major features in map view. It is responsible to show the timeline for a paddock and provides functions to fetch ndvi imagery. This will initiate the controller and make our NDVI panel functional.

Map view — containing web mapping and NDVI imagery list
NDVI panel, controlled by AngularJS ( controller as syntax)

We want to add a new section (Zone/VRT) to this SidePanel to show purchased product on a paddock in a similar way and we are planning to do new developments in Angular (v5). This will help us not add more functionality to AngularJS side and be able to gradually migrate the whole application to Angular platform.

To access Zone/VRT we are going to define a new route 
/#/map/purchased-products?id=farmId

When we access this route an Angular component called PurchasedProductComponent is rendered which shows the list of purchased products.

Purchased products tab controlled by Angular(v5)

Map view is common between these two tabs. We like to somehow detach the map view from NDVI and Zone/VRT tab so we can independently access this tabs by separate routing without losing/changing map context. This is the architecture we are aiming for.

Our AngularJS service for map (Maps.src.js) has some helper methods to deal with the map creation. In the AngularJS the controller responsible for map is called map.src.js. When this controller is instantiated it will create an instance of openlayers map using the Map service. Map service keeps a reference to this instance internally so we can pass the openlayers map instance around as required by different controllers.

To share map between Angular and AngularJS we can use Map service but the issue is if we tie our map to a routing path in AngularJS that controller will be destroyed as we move to a different path and accessing the map route again will try to instantiate another map. As log as we are in the context of the map from user point of view we are interested to keep the same map instance regardless of routing path.

There are multiple reasons for it,

  • Instantiating map is an expensive operation for DOM.
  • Recreating map would cause the map to flicker and it’s not a nice user experience.
  • We will lose map state as this is just a in memory state.
  • it’s unnecessary

To be able to control map lifecycle we need to detach it from the routing path. We need to use <ng-include> and controller assyntax to create and manage the map.

Instantiating MapCtrl using controller as syntax

This way AngularJS will create the map as soon as the index.html is rendered. We need to make some changes and use couple of $rootScope variables to make sure map view is only displayed when we want it. Also with some css tweaks we can make sure that NDVI and ZONE/VRT tabs are rendered in a correct order(z-index) compared to map view to achieve the correct stacking order.

Now we have broken our app into three separate apps:

  • Map view app (AngularJS)
  • The old AngularJS app including NDVI tab which is accessible through old map route path (/#/map?id=farmId)
  • Angular app including new features like ZONE/VRT tab which is accessible through a new route path (/#/map/purchased-products?id=farmId)
Note: Using CustomeUrlHandlingStrategy we can fully control how do we want Angular to handle different routes. In our case any route defined as part of Angular routes will be handled by Angular app, otherwise Angular ignores it and AngularJs app will handle it.

As mentioned now the map view is running independently and for other 2 apps to be able to communicate to map we use a shared service (Map service).

Next step

We are now successfully running these 3 apps side by side and it is deployed to production. Our Angular app is growing fast with new features. 😊

As any other production apps you need properly build your app to optimise and minimise the JavaScript files.

Angular comes configured with powerful webpack. In part 3 we are going to cover how to utilise webpack to build both Angular and AngularJS app as one app and make it production ready.

Link to part 3 for build and deployment. 😊

https://medium.com/sv-blog/running-angularjs-1-6-in-angular-5-side-by-side-part-3-build-deploy-and-beyond-2729c4a032ac