AngularJS à Angular

 

William Marques

William Marques

  • Freelance
  • JHipster Angular Stream Leader
  • JavaScript Lover

 

 

 

@wylmarq

Nomenclature

  • AngularJS: v1 en 2012
  • Angular 2: Septembre 2016

Ces deux frameworks n'ont rien à voir !

AngularJS: Angular version 1.x

Angular: Angular version 2+

Pourquoi migrer ?

Depuis Juillet 2018, AngularJS est entré en période LTS.

 

Les seules mises à jours concerneront:

  • Patch de sécurité
  • Incompatibilité avec jQuery
  • Bugs suite à des mises à jour navigateur

Après le 30 juin 2021, AngularJS ne sera plus maintenu !

D'autres raisons

  • Meilleures performances
  • Support TypeScript
  • Web Components
  • Angular CLI
  • Programmation réactive avec RxJS
  • Recrutement

Notre cas

Les contraintes

  • Migration en best effort
  • Migration incrémentale
  • Livraison de features

Notre choix: NgUpgrade

  • Permet de faire cohabiter AngularJS et Angular
  • Système de downgrade/upgrade des composants, services

Taille du JS

Somme de la taille des fichiers JavaScript fournis lors du chargement de la page d'accueil

AngularJS: 4.3Mo

Angular: 1.4Mo

  • Lazy Loading des locales
  • Utilisation de l'Ahead Of Time Compilation

Avant de migrer...

Pré-requis

  • Utiliser au maximum les composants AngularJS
  • Utiliser webpack comme outil de build
  • Limiter les dépendances à $rootScope

C'est parti !

Démarrage en mode hybride

import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule,
  ],
})
export class AppModule {
  constructor(private upgrade: UpgradeModule) { }
  ngDoBootstrap() {
    this.upgrade.bootstrap(document.body, [AppModule]);
  }
}

Fichier main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Ng2AppModule } from './app.ng.module';

platformBrowserDynamic().bootstrapModule(Ng2AppModule);

Faire cohabiter AngularJS et Angular

Ce qui est possible

  • Downgrader un composant Angular
  • Upgrader un composant AngularJS
  • Downgrader un service Angular
  • Upgrader un service AngularJS

Ce qui n'est pas possible

  • Upgrader un filtre AngularJS
  • Downgrader un pipe Angular
  • Upgrader/downgrader une directive

Downgrader un composant Angular

@Component({
  selector: 'home',
  templateUrl: 'home.html',
  styleUrls: ['home.scss']
})
export class HomeComponent implements OnInit {
  constructor() {}

  ngOnInit() {}
}

@NgModule({
  imports: [SharedModule],
  declarations: [HomeComponent]
})
export class HomeNgModule {}

export const HomeNg1Module = angular.module('myApp.home', []).directive(
  'home',
  downgradeComponent({
    component: HomeComponent
  })
).name;

Upgrader un composant AngularJS

export const HomeComponent = {
  template: `
        <div class="container">
            This is my home
        </div>        
    `,
  controller: class HomeController {}
};

export const HomeNg1Module = angular.module('myApp.home', []).component(
  'home',
  HomeComponent)
).name;

@Directive({
  selector: 'home'
})
export class UpgradedHomeComponent extends UpgradeComponent {
  @Input() text: string;

  constructor(elementRef: ElementRef, injector: Injector) {
    super('home', elementRef, injector);
  }
}

@NgModule({
  imports: [SharedModule],
  declarations: [UpgradedHomeComponent]
})
export class HomeNgModule {}

Downgrader un service Angular

@Injectable({ providedIn: 'root' })
export class HeroService {
  constructor(private http: HttpClient) {}
  
  getHeroes() {
    // Getting heroes
  }
}

export const MyModule = angular
  .module('myApp.hero', [])
  .factory('heroService', downgradeInjectable(HeroService)).name;

Upgrader un service AngularJS (1)

export class HeroService {
  constructor($http) {
    'ngInject';
    this.$http = $http;
  }

  getHeroes() {
    // Getting heroes
  }
}

export default angular
  .module('myApp.hero', [])
  .service('heroService', HeroService)

Upgrader un service AngularJS (2)

export function heroServiceFactory(i) {
  return i.get('heroService');
}

@NgModule({
  imports: [SharedModule],
  declarations: [HeaderComponent],
  providers: [{
    provide: 'heroService',
    useFactory: heroServiceFactory,
    deps: ['$injector']
  },]
})
export class HeroNgModule {}



@Component({
  selector: 'header',
  templateUrl: 'header.html',
  styleUrls: ['header.scss']
})
export class HeaderComponent implements OnInit {
  constructor(
    @Inject('heroService') private heroService
  ) {}

  ngOnInit() {}
}

Le routeur

Deux options

  • Faire cohabiter les deux routeurs
  • Tout migrer à la fin

Notre choix

Migrer tout à la fin:

  • Configuration plus simple
  • Pas d'exception à maintenir sur les deux routeurs

Pas si évident...

Principaux problèmes

  • Régressions: compatibilité IE, effets de bords
  • Equivalence de librairies
  • Spaghetti code : trop de downgrade/upgrade

Recommandations

  • Ecrivez des tests et / ou end-to-end
  • Commencez par les pages les moins critiques
  • Typez tout votre code

Merci !

AngularJS à Angular

By William Marques

AngularJS à Angular

  • 457