Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Migrating a 1M+ LOC project from AngularJS to Angular

262 views

Published on

AngularConnect 2017 presentation on migrating the Google Cloud Console from AngularJS to Angular.

Published in: Technology
  • Be the first to comment

Migrating a 1M+ LOC project from AngularJS to Angular

  1. 1. ● ○ ○ ○ ○
  2. 2. ● ● ● ● ●
  3. 3. ● ● ○ ○ ●
  4. 4. ● ● ●
  5. 5. import { downgradeModule } from '@angular/upgrade/static'; import { ng1App } from './ng1_app'; import { Ng2AppModule } from './ng2_app_module'; const ng2Module = downgradeModule(Ng2AppModule); bootstrap(document.body, [ng1App.name, ng2Module, ...]);
  6. 6. Downgraded ng2 components
  7. 7. @Component({ selector: 'ng2-router-root', template: '<router-outlet></router-outlet>', }) export class Ng2RouterRoot {} @NgModule({ declarations: [ Ng2RouterRoot ], entryComponents: [ Ng2RouterRoot ], exports: [ Ng2RouterRoot ], }) export class Ng2RouterRootNg2Module {}
  8. 8. export const routerRootModule = angular .module('routerRootModule', []) .directive('ng2RouterRoot', downgradeComponent({ component: Ng2RouterRoot, outputs: ['ng2RouterRoot'], propagateDigest: false }) as ng.IDirectiveFactory); <ng2-router-root></ng2-router-root>
  9. 9. angular.module('NavWidgetNg1Module', []) .directive( 'NavWidget', downgradeComponent({ component: NavWidget, inputs: ['property'] }) as ng.IDirectiveFactory); <shell-widget property=“true”></shell-widget>
  10. 10. ● ● ● ● ●
  11. 11. @NgModule({ ... providers: [ LocationSynchronizer, { provide: UrlSerializer, useClass: CustomUrlSerializer }, ] }) export class Ng2AppModule {}
  12. 12. const NG2_PATHS: Array<string|RegExp> = [ '/product1', '/product2/path', '/feature2/[^/]+/subpath', ]; function isNg2(path: string) { return NG2_PATHS.some( (entry) => (entry instanceof RegExp) ? entry.test(path) : path.startsWith(entry); }
  13. 13. $routeProvider.otherwise({ controller: UnmappedPathController, controllerAs: 'ctrl', template: `<div ng-if=“ctrl.show404”> Not found :-( </div>`, }); class UnmappedPathController { show404 = false; constructor(private readonly $location) { if (isNg2Path($location.url())) return; this.show404 = true; } }
  14. 14. export const ROUTES: Routes = [ {path: '/section1', load: 'section1_module'}, // etc. {matcher: ng1UrlMatcher, component: Ng1Component}, {path: '**', component: PageNotFound}, ]; export function ng1UrlMatcher( url: UrlSegment[], group: UrlSegmentGroup): UrlMatchResult { return {consumed: !isNg2Path(group.toString()) ? [] : url}; } @Component({template: ''}) export class Ng1Component {}
  15. 15. ● ● ● ●
  16. 16. return this.myResourceDao .get({id: this.route.params.id}) .$promise.then((response: Response) => response.items) .then(items: Item[]) => items.map(i => ({ id: i.id, name: i.fullName }))); return this.getResourceObs(this.route.params.id) .map((items: Item[]) => { id: i.id, name: i.fullName });
  17. 17. ● ● ● ●
  18. 18. beforeEach(() => { superProviders: [{ provide: MyResourceService, useValue: jasmine.createSpyObj( 'myResourceService', ['getItem']), }], });
  19. 19. → ● ●
  20. 20. ● ● ● ● ●
  21. 21. prefixed naming view encapsulation container class prefixed naming
  22. 22. ● ● ● ● ● ○ ○ ○ ● ○ ○
  23. 23. ● ● ● ●
  24. 24. ● ● ● ●
  25. 25. UI Components UI Tests Product UI Testing Framework Business Logic Components /Services Testing APIs Styling Utilities Template APIs Component APIs
  26. 26. UI Components UI Tests Product UI Testing Framework Business Logic Components /Services Testing APIs Styling Utilities Template APIs Component APIs
  27. 27. ● ● ● ●
  28. 28. ● ● ● ● ● ●
  29. 29. ● ● ● ●
  30. 30. ● ● ● ● ● →
  31. 31. ● Translating template syntax from AngularJS to Angular ● ● ● ●
  32. 32. UI Components UI Tests Product UI Testing Framework Business Logic Components /Services Testing APIs Styling Utilities Template APIs Component APIs
  33. 33. <md-input-container> <label>State</label> <md-select ng-model="ctrl.userState"> <md-option ng-repeat="state in ctrl.states" ng-value="state.abbrev"> {{state.abbrev}} </md-option> </md-select> </md-input-container> <mat-form-field> <mat-select placeholder="State" [(ngModel)]="userState"> <mat-option *ngFor="let state of states" [value]="state.abbrev"> {{state.abbrev}} </mat-option> </mat-select> </mat-form-field>
  34. 34. ● ○ → ○ → ●
  35. 35. ● ● ● ○
  36. 36. ● ○ ○ ○ ● ●
  37. 37. ● ● ● ●
  38. 38. ● ○ ○ ● ○ ○ ● ○ ○ ● ○ ○

×