$30 off During Our Annual Pro Sale. View Details »

Modernizing Large Frontends with Web Components

Sam Julien
January 31, 2020

Modernizing Large Frontends with Web Components

If you’re an enterprise software developer, you and your team probably struggle to know how or when to migrate their legacy code to current frameworks. Should you burn it all to the ground and start over (who has the time for that?) or let it sit forever, getting more outdated each day? Is there a third way?

Web Components help bridge that gap between legacy and modern code without having to go to either extreme. By creating custom reusable elements, teams are able to add a layer of indirection between the business logic and view layer of applications. This indirection lets legacy code and shiny, new frameworks work together in harmony.

In this talk, you’ll learn the current Web Components landscape and how to evaluate whether to use them for large legacy frontend migrations. You’ll also learn how to overcome architectural challenges of large scale refactoring by looking at a common use of Web Components: using Angular Elements to iteratively migrate AngularJS (1.x) to Angular (2+).

Sam Julien

January 31, 2020
Tweet

More Decks by Sam Julien

Other Decks in Technology

Transcript

  1. Modernizing Large Frontends
    with Web Components

    View Slide

  2. Making New, Shiny Things
    Work with Old, Dusty Things

    View Slide

  3. View Slide

  4. Large scale refactors are hard.

    View Slide

  5. Choosing the right path is critical.

    View Slide

  6. Sam Julien
    @samjulien

    View Slide

  7. Sam Julien
    @samjulien
    Senior Developer Advocate Engineer at Auth0

    View Slide

  8. Sam Julien
    @samjulien
    Senior Developer Advocate Engineer at Auth0
    GDE & Angular Collaborator

    View Slide

  9. Sam Julien
    @samjulien
    Senior Developer Advocate Engineer at Auth0
    GDE & Angular Collaborator
    UpgradingAngularJS.com, Thinkster, Egghead

    View Slide

  10. View Slide

  11. @samjulien

    View Slide

  12. Frontend Philosophy
    @samjulien

    View Slide

  13. Frontend Philosophy
    Web Components
    @samjulien

    View Slide

  14. Frontend Philosophy
    Web Components
    AngularJS to Angular
    @samjulien

    View Slide

  15. Frontend Philosophy
    Web Components
    AngularJS to Angular
    @samjulien

    View Slide

  16. Frontend Philosophy
    Web Components
    AngularJS to Angular
    @samjulien
    View from the plane

    View Slide

  17. Frontend Philosophy
    Web Components
    AngularJS to Angular
    @samjulien
    View from the plane
    View from the parachute

    View Slide

  18. Frontend Philosophy
    Web Components
    AngularJS to Angular
    @samjulien
    View from the plane
    View from the parachute
    View from the grocery store

    View Slide

  19. Modern Frontends

    View Slide

  20. View Layer
    Server
    @samjulien

    View Slide

  21. Razor Templates
    ASP.NET MVC
    @samjulien

    View Slide

  22. View Layer
    Business Logic
    @samjulien

    View Slide

  23. Single Page Applications (SPAs)
    @samjulien

    View Slide

  24. View Slide

  25. Business Logic Mixed into View Layer
    @samjulien

    View Slide

  26. DOM Manipulation
    @samjulien

    View Slide

  27. f(x) = y
    @samjulien

    View Slide

  28. Render Logic
    Inputs
    View
    @samjulien

    View Slide

  29. View Layer
    Business Logic
    @samjulien

    View Slide

  30. JS/HTML/CSS
    JavaScript & APIs
    @samjulien

    View Slide

  31. JS/HTML/CSS
    JavaScript & APIs
    Services or classes on the
    frontend, APIs on the
    backend.
    @samjulien

    View Slide

  32. JS/HTML/CSS
    JavaScript & APIs
    As framework agnostic as
    possible.
    @samjulien

    View Slide

  33. JS/HTML/CSS
    JavaScript & APIs
    @samjulien

    View Slide

  34. View Layer
    Business Logic
    @samjulien

    View Slide

  35. Angular
    Business Logic
    @samjulien

    View Slide

  36. React
    Business Logic
    @samjulien

    View Slide

  37. Componentron 2050
    Business Logic
    @samjulien

    View Slide

  38. Modern JS Frameworks
    @samjulien

    View Slide

  39. Avoid Business Logic in Views
    @samjulien

    View Slide

  40. Web Components

    View Slide

  41. What are Web Components?
    @samjulien

    View Slide

  42. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  43. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  44. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  45. Use Cases

    View Slide

  46. Micro Frontends
    @samjulien

    View Slide

  47. @samjulien

    View Slide

  48. CRM
    @samjulien

    View Slide

  49. CRM
    Web Component
    @samjulien

    View Slide

  50. CRM
    Web Component
    Web Component
    @samjulien

    View Slide

  51. CRM
    Web Component
    Web Component
    Web Component
    @samjulien

    View Slide

  52. Shared Components
    @samjulien

    View Slide

  53. React Team
    Component Library
    Angular Team
    @samjulien

    View Slide

  54. Modernization
    @samjulien

    View Slide

  55. @samjulien

    View Slide

  56. Legacy Code
    @samjulien

    View Slide

  57. Legacy Code
    Web Component
    @samjulien

    View Slide

  58. Legacy Code
    Web Component
    Web Component
    @samjulien

    View Slide

  59. Legacy Code
    Web Component
    Web Component
    Web Component
    @samjulien

    View Slide

  60. “An API for the View Layer”
    Akseli Virtanen (_akseliv)
    @samjulien

    View Slide

  61. View Layer
    Business Logic
    @samjulien

    View Slide

  62. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  63. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  64. View Slide

  65. View Slide

  66. Anatomy of a Custom
    Element

    View Slide

  67. Selector String
    @samjulien

    View Slide

  68. JavaScript Class
    @samjulien

    View Slide

  69. CustomElementRegistry.define()
    @samjulien

    View Slide

  70. Options Object
    @samjulien

    View Slide

  71. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide

  72. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide

  73. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide

  74. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide

  75. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide

  76. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide


  77. @samjulien

    View Slide

  78. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide

  79. customElements.define(
    'my-element', MyElement
    );
    @samjulien

    View Slide


  80. @samjulien

    View Slide

  81. Bells & Whistles
    @samjulien

    View Slide

  82. Attributes
    (Used as inputs.)
    @samjulien

    View Slide

  83. Lifecycle Methods
    @samjulien

    View Slide

  84. connectedCallback() {
    console.log('Custom element added to page.');
    }
    disconnectedCallback() {
    console.log('Custom element removed from page.');
    }
    adoptedCallback() {
    console.log('Custom element moved to new page.');
    }
    attributeChangedCallback(name, oldValue, newValue) {
    console.log('Custom element attributes changed.');
    }
    @samjulien

    View Slide

  85. connectedCallback() {
    console.log('Custom element added to page.');
    }
    disconnectedCallback() {
    console.log('Custom element removed from page.');
    }
    adoptedCallback() {
    console.log('Custom element moved to new page.');
    }
    attributeChangedCallback(name, oldValue, newValue) {
    console.log('Custom element attributes changed.');
    }
    @samjulien

    View Slide

  86. connectedCallback() {
    console.log('Custom element added to page.');
    }
    disconnectedCallback() {
    console.log('Custom element removed from page.');
    }
    adoptedCallback() {
    console.log('Custom element moved to new page.');
    }
    attributeChangedCallback(name, oldValue, newValue) {
    console.log('Custom element attributes changed.');
    }
    @samjulien

    View Slide

  87. connectedCallback() {
    console.log('Custom element added to page.');
    }
    disconnectedCallback() {
    console.log('Custom element removed from page.');
    }
    adoptedCallback() {
    console.log('Custom element moved to new page.');
    }
    attributeChangedCallback(name, oldValue, newValue) {
    console.log('Custom element attributes changed.');
    }
    @samjulien

    View Slide

  88. connectedCallback() {
    console.log('Custom element added to page.');
    }
    disconnectedCallback() {
    console.log('Custom element removed from page.');
    }
    adoptedCallback() {
    console.log('Custom element moved to new page.');
    }
    attributeChangedCallback(name, oldValue, newValue) {
    console.log('Custom element attributes changed.');
    }
    @samjulien

    View Slide

  89. Shadow DOM
    @samjulien

    View Slide

  90. const shadow = this.attachShadow({ mode: 'open' });
    @samjulien

    View Slide

  91. HTML Templates
    @samjulien

    View Slide

  92. and
    @samjulien

    View Slide

  93. It’s complicated…
    @samjulien

    View Slide

  94. View Slide

  95. View Slide

  96. View Slide

  97. Custom Element Libraries

    View Slide

  98. LitElement
    @samjulien

    View Slide

  99. View Slide

  100. @samjulien

    View Slide

  101. Declarative
    @samjulien

    View Slide

  102. Declarative
    Lightweight
    @samjulien

    View Slide

  103. Declarative
    Lightweight
    Interoperable
    @samjulien

    View Slide

  104. import {
    LitElement, html, property, customElement
    } from 'lit-element';
    @customElement('hello-conf')
    export class HelloConf extends LitElement {
    @property() name = ‘Friends';
    render() {
    return html`Hello, ${this.name}!`;
    }
    }

    @samjulien

    View Slide

  105. import {
    LitElement, html, property, customElement
    } from 'lit-element';
    @customElement('hello-conf')
    export class HelloConf extends LitElement {
    @property() name = ‘Friends';
    render() {
    return html`Hello, ${this.name}!`;
    }
    }

    @samjulien

    View Slide

  106. import {
    LitElement, html, property, customElement
    } from 'lit-element';
    @customElement('hello-conf')
    export class HelloConf extends LitElement {
    @property() name = ‘Friends';
    render() {
    return html`Hello, ${this.name}!`;
    }
    }

    @samjulien

    View Slide

  107. import {
    LitElement, html, property, customElement
    } from 'lit-element';
    @customElement('hello-conf')
    export class HelloConf extends LitElement {
    @property() name = ‘Friends';
    render() {
    return html`Hello, ${this.name}!`;
    }
    }

    @samjulien

    View Slide

  108. import {
    LitElement, html, property, customElement
    } from 'lit-element';
    @customElement('hello-conf')
    export class HelloConf extends LitElement {
    @property() name = ‘Friends';
    render() {
    return html`Hello, ${this.name}!`;
    }
    }

    @samjulien

    View Slide

  109. Stencil
    @samjulien

    View Slide

  110. View Slide

  111. @samjulien

    View Slide

  112. Simple
    @samjulien

    View Slide

  113. Simple
    Lightweight
    @samjulien

    View Slide

  114. Simple
    Lightweight
    Cross-Framework
    @samjulien

    View Slide

  115. Angular Elements
    @samjulien

    View Slide

  116. View Slide

  117. @samjulien

    View Slide

  118. Uses Angular
    @samjulien

    View Slide

  119. Uses Angular
    Powerful
    @samjulien

    View Slide

  120. Uses Angular
    Powerful
    Hooks into AngularJS
    @samjulien

    View Slide

  121. Modernization
    @samjulien

    View Slide

  122. AngularJS to Angular
    @samjulien

    View Slide

  123. [Legacy] to Angular
    @samjulien

    View Slide

  124. [Legacy] to [New Framework]
    @samjulien

    View Slide

  125. AngularJS to Angular

    View Slide

  126. @samjulien

    View Slide

  127. The Four Paths
    @samjulien

    View Slide

  128. The Four Paths
    Rewrite
    @samjulien

    View Slide

  129. The Four Paths
    ngUpgrade
    Rewrite
    @samjulien

    View Slide

  130. The Four Paths
    Hybrid Routing
    ngUpgrade
    Rewrite
    @samjulien

    View Slide

  131. The Four Paths
    Hybrid Routing Angular Elements
    ngUpgrade
    Rewrite
    @samjulien

    View Slide

  132. Angular Elements
    @samjulien

    View Slide

  133. Why Angular Elements?

    View Slide

  134. Why not ngUpgrade?

    View Slide

  135. Where ngUpgrade Works
    @samjulien

    View Slide

  136. Where ngUpgrade Works
    Small to Medium Apps
    @samjulien

    View Slide

  137. Where ngUpgrade Works
    Small to Medium Apps
    Low Complexity
    @samjulien

    View Slide

  138. Where ngUpgrade Works
    Small to Medium Apps
    Low Complexity
    Modern AngularJS
    @samjulien

    View Slide

  139. But…
    @samjulien

    View Slide

  140. Tightly Coupled
    @samjulien

    View Slide

  141. @samjulien
    Dependency Injection

    View Slide

  142. @samjulien
    Dependency Injection
    Change Detection

    View Slide

  143. @samjulien
    Complex UIs
    Testing ☠

    View Slide

  144. Angular Elements
    @samjulien

    View Slide

  145. Angular Elements
    Large Apps
    @samjulien

    View Slide

  146. Angular Elements
    Large Apps
    High Complexity
    @samjulien

    View Slide

  147. Angular Elements
    Large Apps
    Legacy Patterns
    High Complexity
    @samjulien

    View Slide

  148. The Elements Upgrade Strategy
    @samjulien

    View Slide

  149. The Elements Upgrade Strategy
    Set Up
    ⬆ Bottom-up components
    Create vanilla JS services with wrappers
    Convert routing and remove Elements
    @samjulien

    View Slide

  150. Setting Up Angular Elements

    View Slide

  151. View Slide

  152. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  153. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  154. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  155. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  156. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  157. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  158. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  159. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    Monorepo

    View Slide

  160. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    bundle-ce.js
    Monorepo

    View Slide

  161. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    bundle-ce.js
    Monorepo

    View Slide

  162. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    bundle-ce.js
    polyfills.js
    Monorepo

    View Slide

  163. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    bundle-ce.js
    polyfills.js
    Monorepo

    View Slide

  164. Custom Element
    index.html
    Custom Element
    Custom Element
    Polyfills
    Angular CLI Project AngularJS Project
    bundle-ce.js
    polyfills.js
    Monorepo

    View Slide

  165. Add Angular Elements
    @samjulien

    View Slide

  166. ng add @angular/elements
    @samjulien

    View Slide

  167. ngx-build-plus
    @samjulien

    View Slide

  168. @samjulien

    View Slide

  169. ng add ngx-build-plus
    @samjulien

    View Slide

  170. ng build --prod --single-bundle
    @samjulien

    View Slide

  171. cpr dist/custom-el ../legacy/dist/custom-el -d
    @samjulien

    View Slide

  172. ⚠ Polyfills
    @samjulien

    View Slide

  173. Upgrading Components

    View Slide

  174. @samjulien

    View Slide

  175. @samjulien

    View Slide

  176. @samjulien

    View Slide

  177. customers
    customers-table
    customerService
    @samjulien

    View Slide

  178. customers
    customers-table-ce
    customerService
    @samjulien

    View Slide

  179. @Component({
    template: `

    // ...

    `
    })
    export class CustomersTableComponent {
    @Input() customers;
    constructor() {}
    }
    @samjulien

    View Slide

  180. @Component({
    template: `

    // ...

    `
    })
    export class CustomersTableComponent {
    @Input() customers;
    constructor() {}
    }
    @samjulien

    View Slide

  181. @Component({
    template: `

    // ...

    `
    })
    export class CustomersTableComponent {
    @Input() customers;
    constructor() {}
    }
    @samjulien

    View Slide

  182. @Component({
    template: `

    // ...

    `
    })
    export class CustomersTableComponent {
    @Input() customers;
    constructor() {}
    }
    @samjulien

    View Slide

  183. Cool, but how do we use it?

    View Slide

  184. @NgModule({
    entryComponents: [CustomersTableComponent],
    declarations: [CustomersTableComponent]
    // ...Providers, etc.
    })
    @samjulien

    View Slide

  185. @NgModule({
    entryComponents: [CustomersTableComponent],
    declarations: [CustomersTableComponent]
    // ...Providers, etc.
    })
    @samjulien

    View Slide

  186. @NgModule({
    entryComponents: [CustomersTableComponent],
    declarations: [CustomersTableComponent]
    // ...Providers, etc.
    })
    @samjulien

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  192. How do we pass data?

    View Slide

  193. AngularJS 1.7+ Helpers
    @samjulien

    View Slide

  194. ng-custom-element
    @samjulien

    View Slide

  195. Inputs

    View Slide

  196. ng-prop-*
    @samjulien

    View Slide

  197. @Component({
    template: `

    // ...

    `
    })
    export class CustomersTableComponent {
    @Input() customers;
    constructor() {}
    }
    @samjulien

    View Slide

  198. ng-prop-customers=“$ctrl.customers”>

    @samjulien

    View Slide

  199. ng-prop-customers=“$ctrl.customers”>

    @samjulien

    View Slide

  200. ng-prop-customers=“$ctrl.customers”>

    @samjulien

    View Slide

  201. How do get data out?

    View Slide

  202. Outputs

    View Slide

  203. @samjulien

    View Slide

  204. @samjulien

    View Slide

  205. @samjulien

    View Slide

  206. @samjulien

    View Slide

  207. @samjulien

    View Slide

  208. customer-detail
    discount
    @samjulien

    View Slide

  209. customer-detail
    discount-ce
    @samjulien

    View Slide

  210. 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

    View Slide

  211. 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

    View Slide

  212. 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

    View Slide

  213. 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

    View Slide

  214. ng-on-*
    @samjulien

    View Slide

  215. ng-if="$ctrl.customer.getsDiscount"
    ng-prop-customer_discount="$ctrl.customer.discount"
    ng-on-update_discount="$ctrl.updateDiscount($event)"
    >
    @samjulien

    View Slide

  216. ng-if="$ctrl.customer.getsDiscount"
    ng-prop-customer_discount="$ctrl.customer.discount"
    ng-on-update_discount="$ctrl.updateDiscount($event)"
    >
    @samjulien

    View Slide

  217. ng-if="$ctrl.customer.getsDiscount"
    ng-prop-customer_discount="$ctrl.customer.discount"
    ng-on-update_discount="$ctrl.updateDiscount($event)"
    >
    @samjulien

    View Slide

  218. ng-if="$ctrl.customer.getsDiscount"
    ng-prop-customer_discount="$ctrl.customer.discount"
    ng-on-update_discount="$ctrl.updateDiscount($event)"
    >
    @samjulien

    View Slide

  219. How do we register multiple elements?

    View Slide

  220. 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

    View Slide

  221. 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

    View Slide

  222. 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

    View Slide

  223. 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

    View Slide

  224. 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

    View Slide

  225. 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

    View Slide

  226. 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

    View Slide

  227. Tip: Group into Modules
    @samjulien

    View Slide

  228. Sharing Data with Services

    View Slide

  229. Here be dragons
    @samjulien

    View Slide

  230. Off-Roading with JS
    @samjulien

    View Slide

  231. Wrap Shared JS Services
    @samjulien

    View Slide

  232. Vanilla JS Service
    @samjulien

    View Slide

  233. Vanilla JS Service
    AngularJS Wrapper
    Angular Wrapper
    @samjulien

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  243. export class AngularJSCustomerService {
    readonly customerService: CustomerService;
    constructor() {
    this.customerService =
    CustomerService.sharedInstance();
    }
    }
    @samjulien

    View Slide

  244. export class AngularJSCustomerService {
    readonly customerService: CustomerService;
    constructor() {
    this.customerService =
    CustomerService.sharedInstance();
    }
    }
    @samjulien

    View Slide

  245. export class AngularJSCustomerService {
    readonly customerService: CustomerService;
    constructor() {
    this.customerService =
    CustomerService.sharedInstance();
    }
    }
    @samjulien

    View Slide

  246. customerService.getCustomers()
    @samjulien

    View Slide

  247. getCustomers(){
    return from(
    this.customerService.getCustomers()
    );
    }
    @samjulien

    View Slide

  248. getCustomers(){
    return from(
    this.customerService.getCustomers()
    );
    }
    @samjulien

    View Slide

  249. getCustomers(){
    return from(
    this.customerService.getCustomers()
    );
    }
    @samjulien

    View Slide

  250. getCustomers(){
    return this.customerService.getCustomers()
    .then(() => {
    $rootScope.apply();
    });
    }
    @samjulien

    View Slide

  251. getCustomers(){
    return this.customerService.getCustomers()
    .then(() => {
    $rootScope.apply();
    });
    }
    @samjulien

    View Slide

  252. getCustomers(){
    return this.customerService.getCustomers()
    .then(() => {
    $rootScope.apply();
    });
    }
    @samjulien

    View Slide

  253. getCustomers(){
    return this.customerService.getCustomers()
    .then(() => {
    $rootScope.apply();
    });
    }
    @samjulien

    View Slide

  254. getCustomers(){
    return this.customerService.getCustomers()
    .then(() => {
    $rootScope.apply();
    });
    }
    @samjulien

    View Slide

  255. Use inputs and outputs as much as possible.
    @samjulien

    View Slide

  256. Inputs & Outputs
    @samjulien

    View Slide

  257. Indirection
    @samjulien

    View Slide

  258. ,
    .
    @samjulien

    View Slide

  259. NgRx & Redux
    @samjulien

    View Slide

  260. Angular Elements
    @samjulien

    View Slide

  261. Angular Elements
    Large Apps
    Legacy Patterns
    High Complexity
    @samjulien

    View Slide

  262. Angular Elements
    Large Apps
    Legacy Patterns
    High Complexity
    @samjulien

    View Slide

  263. NgRx
    Large Apps
    High Complexity
    @samjulien

    View Slide

  264. Wrapping Up the Upgrade

    View Slide

  265. Add Component Selectors
    @samjulien

    View Slide

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

    // ...

    `
    })
    export class CustomersTableComponent {}

    View Slide

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

    // ...

    `
    })
    export class CustomersTableComponent {}

    View Slide

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

    // ...

    `
    })
    export class CustomersTableComponent {}

    View Slide

  269. Switch Routing
    @samjulien

    View Slide

  270. Drop Elements
    @samjulien

    View Slide

  271. Drop Elements
    Clean up modules
    Uninstall Elements and tools
    Remove unused polyfills
    @samjulien

    View Slide

  272. Let’s Review

    View Slide

  273. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  274. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  275. "Web Components is a suite of different technologies
    allowing you to create reusable custom elements —
    with their functionality encapsulated away from the
    rest of your code — and utilize them in your web apps.”
    - MDN Web Docs
    @samjulien

    View Slide

  276. customElements.define(
    'my-element', MyElement, { extends: 'p' }
    );
    @samjulien

    View Slide

  277. View Slide

  278. Angular Elements
    @samjulien

    View Slide

  279. The Elements Upgrade Strategy
    Set Up
    ⬆ Bottom-up components
    Create vanilla JS services with wrappers
    Convert routing and remove Elements
    @samjulien

    View Slide

  280. 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

    View Slide

  281. samj.im/ndc-london
    @samjulien

    View Slide

  282. samj.im/ndc-london
    Thank you!
    @samjulien

    View Slide