Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bridging Parallel Universes: Upgrading with Ang...

Sam Julien
August 01, 2019

Bridging Parallel Universes: Upgrading with Angular Elements (Angular Denver 2019)

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!

Sam Julien

August 01, 2019
Tweet

More Decks by Sam Julien

Other Decks in Technology

Transcript

  1. Sam Julien @samjulien Technical Community Manager at Auth0 GDE &

    Angular Collaborator UpgradingAngularJS.com @samjulien
  2. What are Angular Elements? Custom Web Components Tiny Angular Apps

    “Fill in the Gaps” of Angular use cases @samjulien
  3. The Elements Upgrade Strategy Set Up ⬆ Bottom-up components Create

    vanilla JS services with wrappers Convert routing and remove Elements @samjulien
  4. Custom Element index.html Custom Element Custom Element Polyfills Angular CLI

    Project AngularJS Project bundle-ce.js polyfills.js Monorepo
  5. @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  6. @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  7. @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  8. @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  9. export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  10. export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  11. export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  12. export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  13. export class AppModule { constructor(private injector: Injector) {} ngDoBootstrap() {

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  14. @Component({ template: ` <table> // ... </table> ` }) export

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  27. let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  28. let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  29. let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  30. let lazySharedInstance: CustomerService | undefined; export class CustomerService { static

    sharedInstance() { if (!lazySharedInstance) { lazySharedInstance = new CustomerService(); } return lazySharedInstance; } } @samjulien
  31. @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  32. @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  33. @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  34. @Injectable({ providedIn: 'root' }) export class AngularCustomerService { readonly customerService:

    CustomerService; constructor() { this.customerService = CustomerService.sharedInstance(); } } @samjulien
  35. @samjulien @Component({ // selector: 'customers-table' template: ` <table> // ...

    </table> ` }) export class CustomersTableComponent {}
  36. @samjulien @Component({ // selector: 'customers-table' template: ` <table> // ...

    </table> ` }) export class CustomersTableComponent {}
  37. The Elements Upgrade Strategy Set Up ⬆ Bottom-up components Create

    vanilla JS services with wrappers Convert routing and remove Elements @samjulien
  38. 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