Bridging Parallel Universes: Upgrading from AngularJS with Angular Elements (AngularMix)

7beed3a6fa39e12c9e873b903e4d9244?s=47 Sam Julien
November 21, 2019

Bridging Parallel Universes: Upgrading from AngularJS with Angular Elements (AngularMix)

How do you jump between the AngularJS universe and the Angular universe without tearing the fabric of reality? If you've got a giant AngularJS application, you've probably been stumped (and frustrated!) by how to get it moved to Angular. ngUpgrade doesn't seem to be a good option for you and you roll your eyes at anyone who suggests rewriting this behemoth from scratch. What are you supposed to do?

Luckily, there's a new kid on the block that's here to help: Angular Elements. Angular Elements lets you use tiny Angular apps as reusable custom web elements, which happens to be an excellent strategy for migrating big applications from AngularJS. Until now, though, there hasn't been much real-world content on HOW to actually do this. Sam Julien, Mad Upgrade Scientist, is here to help! In this talk, you'll learn how to plan your migration with Angular Elements, how to migrate components and services, and tips on how to bundle your custom elements. You'll leave this talk feeling like the master of the upgrade multiverse!

7beed3a6fa39e12c9e873b903e4d9244?s=128

Sam Julien

November 21, 2019
Tweet

Transcript

  1. 8.

    Sam Julien @samjulien Developer Advocate Engineer at Auth0 GDE &

    Angular Collaborator UpgradingAngularJS.com, Thinkster, Egghead @samjulien
  2. 17.

    What are Angular Elements? Custom Web Components Tiny Angular Apps

    “Fill in the Gaps” of Angular use cases @samjulien
  3. 34.

    The Elements Upgrade Strategy Set Up ⬆ Bottom-up components Create

    vanilla JS services with wrappers Convert routing and remove Elements @samjulien
  4. 35.
  5. 36.
  6. 46.
  7. 47.
  8. 48.

    Custom Element index.html Custom Element Custom Element Polyfills Angular CLI

    Project AngularJS Project bundle-ce.js polyfills.js Monorepo
  9. 49.

    Custom Element index.html Custom Element Custom Element Polyfills Angular CLI

    Project AngularJS Project bundle-ce.js polyfills.js Monorepo
  10. 50.

    Custom Element index.html Custom Element Custom Element Polyfills Angular CLI

    Project AngularJS Project bundle-ce.js polyfills.js Monorepo
  11. 65.

    @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  12. 66.

    @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  13. 67.

    @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  14. 68.

    @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  15. 73.

    export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  16. 74.

    export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  17. 75.

    export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  18. 76.

    export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  19. 77.

    export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  20. 81.
  21. 83.

    @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  22. 88.
  23. 96.

    export class DiscountComponent { @Input() customerDiscount: any; @Output() updateDiscount =

    new EventEmitter(); selectedDiscount: any; // ...other code updateDiscountType() { this.updateDiscount.emit({ discount: this.selectedDiscount }); this.editDiscount = false; } } @samjulien
  24. 97.

    export class DiscountComponent { @Input() customerDiscount: any; @Output() updateDiscount =

    new EventEmitter(); selectedDiscount: any; // ...other code updateDiscountType() { this.updateDiscount.emit({ discount: this.selectedDiscount }); this.editDiscount = false; } } @samjulien
  25. 98.

    export class DiscountComponent { @Input() customerDiscount: any; @Output() updateDiscount =

    new EventEmitter(); selectedDiscount: any; // ...other code updateDiscountType() { this.updateDiscount.emit({ discount: this.selectedDiscount }); this.editDiscount = false; } } @samjulien
  26. 99.

    export class DiscountComponent { @Input() customerDiscount: any; @Output() updateDiscount =

    new EventEmitter(); selectedDiscount: any; // ...other code updateDiscountType() { this.updateDiscount.emit({ discount: this.selectedDiscount }); this.editDiscount = false; } } @samjulien
  27. 106.

    const elements: any[] = [ [CustomersTableComponent, 'customers-table-ce'], [DiscountComponent, 'discount-ce'] ];

    for (const [component, name] of elements) { const el = createCustomElement(component, { injector: this.injector }); customElements.define(name, el); } @samjulien
  28. 107.

    const elements: any[] = [ [CustomersTableComponent, 'customers-table-ce'], [DiscountComponent, 'discount-ce'] ];

    for (const [component, name] of elements) { const el = createCustomElement(component, { injector: this.injector }); customElements.define(name, el); } @samjulien
  29. 108.

    const elements: any[] = [ [CustomersTableComponent, 'customers-table-ce'], [DiscountComponent, 'discount-ce'] ];

    for (const [component, name] of elements) { const el = createCustomElement(component, { injector: this.injector }); customElements.define(name, el); } @samjulien
  30. 109.

    const elements: any[] = [ [CustomersTableComponent, 'customers-table-ce'], [DiscountComponent, 'discount-ce'] ];

    for (const [component, name] of elements) { const el = createCustomElement(component, { injector: this.injector }); customElements.define(name, el); } @samjulien
  31. 110.

    const elements: any[] = [ [CustomersTableComponent, 'customers-table-ce'], [DiscountComponent, 'discount-ce'] ];

    for (const [component, name] of elements) { const el = createCustomElement(component, { injector: this.injector }); customElements.define(name, el); } @samjulien
  32. 111.

    const elements: any[] = [ [CustomersTableComponent, 'customers-table-ce'], [DiscountComponent, 'discount-ce'] ];

    for (const [component, name] of elements) { const el = createCustomElement(component, { injector: this.injector }); customElements.define(name, el); } @samjulien
  33. 112.

    const elements: any[] = [ [CustomersTableComponent, 'customers-table-ce'], [DiscountComponent, 'discount-ce'] ];

    for (const [component, name] of elements) { const el = createCustomElement(component, { injector: this.injector }); customElements.define(name, el); } @samjulien
  34. 116.
  35. 117.
  36. 121.

    let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  37. 122.

    let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  38. 123.

    let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  39. 124.

    let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  40. 125.

    let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  41. 126.

    @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  42. 127.

    @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  43. 128.

    @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  44. 129.

    @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  45. 153.

    @samjulien @Component({ // selector: ‘customers-table', template: ` <table> // ...

    </table> ` }) export class CustomersTableComponent {}
  46. 154.

    @samjulien @Component({ // selector: ‘customers-table', template: ` <table> // ...

    </table> ` }) export class CustomersTableComponent {}
  47. 159.

    The Elements Upgrade Strategy Set Up ⬆ Bottom-up components Create

    vanilla JS services with wrappers Convert routing and remove Elements @samjulien
  48. 160.

    Where to Go from Here Erin Coughlan: • AngularConnect 2018

    Talk • Devoxx Belgium 2018 Talk • create-ng1-wrapper Juri Strumpflohner • Egghead Course Manfred Steyer • Upgrading with Elements • Elements Series • A Deep Look at Elements • Beyond the Basics @samjulien