Migrate AngularJS to Angular using UpgradeModule (Part 5)

Danny Cornelisse
10-12-2018

Part 1: Setting up the hybird app

Part 2: Refactor constants, values and services

Refactoring angularJS to Angular

The previous steps explained how to set up the hybrid application (step 1), how to refactor constants, values and services (step 2), how to refactor components (step 3) and how to refactor directives (step 4). This next step will dive into phasing out angularJS from the hybrid app.
 

Phase out angularJS

Angular helper/utility functions

AngularJS has a number of helper functions that can be used as helpers in javascript code:

https://docs.angularjs.org/api/ng/function

These methods are bound to the global angular object. Most used functions can be:

  • isDefined
  • forEach
  • copy
  • isObject / angular.isString / angular.isDate

For each method, find a replacement. For instance, ES6 has a forEach method on the Object and Array prototypes. RxJS, a commonly used library for observables in Angular, also ships with utils:

import { isObject } from 'rxjs/util/isObject';

 

Replace angularJS services

The next step to be taken is to replace the native angularJS services with their respective equivalents. Some angularJS services and their equivalent:

angularJS name Name Package
$location Location @angular/common
$filter @pipe Own implementation
$q Promise Native ES6
$scope Component member @component
$rootScope Angular service @angular
$sce DomSanitizer, SafeResourceUr @angular/platform-browser
$interval interval rxjs/observable/interval
$timeout timer rxjs/observable/timer
$http HttpClient @angular/common/http

 

After replacing the angularJS services, remove them from the providers in app.module.ts.

 

$http to HttpClient

The key difference between $http and HttpClient is the fact that $http returns a Promise, whereas HttpClient returns an Observable, that has be subscribed to.
Refactor angularJS $http to Angular HttpClient:

// angularJS

function NewService ($http) {
  const svc = this;

  svc.doFetch = function () {
    return $http.get('my-api')
      .then(data => svc.data = data)
      .catch(err => console.log(err));
  }
…

To:

// Angular
import { HttpClient } from '@angular/common/http';

@Injectable()
export class NewService {
  constructor (
    public http: HttpClient
  ) {}

  doFetch() {
    return this.http.get('my-api')
      .map(res => res.json())
      .subscribe(
        data => this.data = data,
        err => console.log(err)
      );
  }
…

Chaining promises in angularJS is simple. Keep chaining .then statements, as long as each function returns a promise:

// angularJS

svc.doFetch = function () {
  return $http.get('my-first-api')
    .then(data => $http.get(`my-second-api-${data}`))
    .then(newData => this.newData = newData)
    .catch(err => console.log(err));
}
…

For chaining http calls in Angular, either nest subscriptions, or use concatMap:

this.http.get('my-first-api')
  .pipe(
    concatMap(data => {
      return this.http.get((`my-second-api-${data}`);
    }),
    concatMap(res => {
      return this.http.get('https://pokeapi.co/api/v2/pokemon/3/');
    })
  )
  .subscribe(newData => console.log(newData));

Rxjs Observables are very powerful. For a good article about mapping observables:
https://medium.com/@shairez/a-super-ninja-trick-to-learn-rxjss-switchmap-mergemap-concatmap-and-exhaustmap-forever-88e178a75f1b

Note: Observables and Promises are very different in functionality. They might affect the frontend structure and architecture. However, it is feasible to refactor $http to HttpClient using observables, as described above.

 

Events and $rootScope

Events in Angular are implemented using EventEmitter. In angularJS, the following pattern may have been used:

// new.service.js

function NewService ($rootScope) {
  const svc = this;    
   this.newServiceEvent = $rootScope.$broadcast('new-event', 'hello');
…

// new.controller.js

function NewController ($rootScope) {
  const vm = this;
  vm.eventSub = $rootScope.$on('new-event', mess => console.log(mess));
  
  vm.$onDestroy () {
    vm.eventSub();
    vm.eventSub = null;
  }

In Angular, refactor this to:

// new.service.ts

import { Injectable, Inject, EventEmitter } from '@angular/core';

@Injectable()
export class NewService {
    public newServiceEvent = new EventEmitter();
…

// new.component.ts

…
export class NewComponent implements OnInit, OnDestroy{
  private eventSub;
  constructor(
    public newService: NewService
  ) {
    // Register subscriber before it emits a value!
    this.eventSub = this.newService.newServiceEvent
      .subscribe(mess => console.log(mess));
  }

  ngOnInit () {
    this.newService.newServiceEvent.emit('hello');
 }
  ngOnDestroy () {
    this.newService.newServiceEvent.unsubscribe();
  }

For an alternative use of BehaviorSubject (from rxjs) to implement an Observable based subscription, please see:

https://stackoverflow.com/questions/34376854/delegation-eventemitter-or-observable-in-angular/35568924#35568924

In the next part, we’ll remove/replace the angularJS dependent libraries and then remove angularJS entirely from the hybrid app.

LEAVE A REPLY

you might also like