Modernizing Large Frontends with Web Components

7beed3a6fa39e12c9e873b903e4d9244?s=47 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+).

7beed3a6fa39e12c9e873b903e4d9244?s=128

Sam Julien

January 31, 2020
Tweet

Transcript

  1. Modernizing Large Frontends with Web Components

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

  3. None
  4. Large scale refactors are hard.

  5. Choosing the right path is critical.

  6. Sam Julien @samjulien

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

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

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

    & Angular Collaborator UpgradingAngularJS.com, Thinkster, Egghead
  10. None
  11. @samjulien

  12. Frontend Philosophy @samjulien

  13. Frontend Philosophy Web Components @samjulien

  14. Frontend Philosophy Web Components AngularJS to Angular @samjulien

  15. Frontend Philosophy Web Components AngularJS to Angular @samjulien

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

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

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

    the plane View from the parachute View from the grocery store
  19. Modern Frontends

  20. View Layer Server @samjulien

  21. Razor Templates ASP.NET MVC @samjulien

  22. View Layer Business Logic @samjulien

  23. Single Page Applications (SPAs) @samjulien

  24. None
  25. Business Logic Mixed into View Layer @samjulien

  26. DOM Manipulation @samjulien

  27. f(x) = y @samjulien

  28. Render Logic Inputs View @samjulien

  29. View Layer Business Logic @samjulien

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

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

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

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

  34. View Layer Business Logic @samjulien

  35. Angular Business Logic @samjulien

  36. React Business Logic @samjulien

  37. Componentron 2050 Business Logic @samjulien

  38. Modern JS Frameworks @samjulien

  39. Avoid Business Logic in Views @samjulien

  40. Web Components

  41. What are Web Components? @samjulien

  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
  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
  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
  45. Use Cases

  46. Micro Frontends @samjulien

  47. @samjulien

  48. CRM @samjulien

  49. CRM Web Component @samjulien

  50. CRM Web Component Web Component @samjulien

  51. CRM Web Component Web Component Web Component @samjulien

  52. Shared Components @samjulien

  53. React Team Component Library Angular Team @samjulien

  54. Modernization @samjulien

  55. @samjulien

  56. Legacy Code @samjulien

  57. Legacy Code Web Component @samjulien

  58. Legacy Code Web Component Web Component @samjulien

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

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

  61. View Layer Business Logic @samjulien

  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
  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
  64. None
  65. None
  66. Anatomy of a Custom Element

  67. Selector String @samjulien

  68. JavaScript Class @samjulien

  69. CustomElementRegistry.define() @samjulien

  70. Options Object @samjulien

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

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

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

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

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

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

  77. <p is=“my-element”></p> @samjulien

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

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

  80. <my-element></my-element> @samjulien

  81. Bells & Whistles @samjulien

  82. Attributes (Used as inputs.) @samjulien

  83. Lifecycle Methods @samjulien

  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
  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
  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
  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
  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
  89. Shadow DOM @samjulien

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

  91. HTML Templates @samjulien

  92. <template> and <slot> @samjulien

  93. It’s complicated… @samjulien

  94. None
  95. None
  96. None
  97. Custom Element Libraries

  98. LitElement @samjulien

  99. None
  100. @samjulien

  101. Declarative @samjulien

  102. Declarative Lightweight @samjulien

  103. Declarative Lightweight Interoperable @samjulien

  104. import { LitElement, html, property, customElement } from 'lit-element'; @customElement('hello-conf')

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

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

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

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

    export class HelloConf extends LitElement { @property() name = ‘Friends'; render() { return html`<p>Hello, ${this.name}!</p>`; } } <hello-conf name=“NDC London”></hello-conf> @samjulien
  109. Stencil @samjulien

  110. None
  111. @samjulien

  112. Simple @samjulien

  113. Simple Lightweight @samjulien

  114. Simple Lightweight Cross-Framework @samjulien

  115. Angular Elements @samjulien

  116. None
  117. @samjulien

  118. Uses Angular @samjulien

  119. Uses Angular Powerful @samjulien

  120. Uses Angular Powerful Hooks into AngularJS @samjulien

  121. Modernization @samjulien

  122. AngularJS to Angular @samjulien

  123. [Legacy] to Angular @samjulien

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

  125. AngularJS to Angular

  126. @samjulien

  127. The Four Paths @samjulien

  128. The Four Paths Rewrite @samjulien

  129. The Four Paths ngUpgrade Rewrite @samjulien

  130. The Four Paths Hybrid Routing ngUpgrade Rewrite @samjulien

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

  132. Angular Elements @samjulien

  133. Why Angular Elements?

  134. Why not ngUpgrade?

  135. Where ngUpgrade Works @samjulien

  136. Where ngUpgrade Works Small to Medium Apps @samjulien

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

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

    AngularJS @samjulien
  139. But… @samjulien

  140. Tightly Coupled @samjulien

  141. @samjulien Dependency Injection

  142. @samjulien Dependency Injection Change Detection

  143. @samjulien Complex UIs Testing ☠

  144. Angular Elements @samjulien

  145. Angular Elements Large Apps @samjulien

  146. Angular Elements Large Apps High Complexity @samjulien

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

  148. The Elements Upgrade Strategy @samjulien

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

    vanilla JS services with wrappers Convert routing and remove Elements @samjulien
  150. Setting Up Angular Elements

  151. None
  152. Custom Element index.html Custom Element Custom Element Polyfills Angular CLI

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

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

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

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

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

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

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

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

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

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

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

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

    Project AngularJS Project bundle-ce.js polyfills.js Monorepo
  165. Add Angular Elements @samjulien

  166. ng add @angular/elements @samjulien

  167. ngx-build-plus @samjulien

  168. @samjulien

  169. ng add ngx-build-plus @samjulien

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

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

  172. ⚠ Polyfills @samjulien

  173. Upgrading Components

  174. @samjulien

  175. @samjulien

  176. @samjulien

  177. customers customers-table customerService @samjulien

  178. customers customers-table-ce customerService @samjulien

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

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

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

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

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  183. Cool, but how do we use it?

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

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

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

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

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

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

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

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

    const el = createCustomElement(CustomersTableComponent, { injector: this.injector }); customElements.define('customers-table-ce', el); } } @samjulien
  192. How do we pass data?

  193. AngularJS 1.7+ Helpers @samjulien

  194. ng-custom-element @samjulien

  195. Inputs

  196. ng-prop-* @samjulien

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

    class CustomersTableComponent { @Input() customers; constructor() {} } @samjulien
  198. <customers-table-ce ng-prop-customers=“$ctrl.customers”> </customers-table-ce> @samjulien

  199. <customers-table-ce ng-prop-customers=“$ctrl.customers”> </customers-table-ce> @samjulien

  200. <customers-table-ce ng-prop-customers=“$ctrl.customers”> </customers-table-ce> @samjulien

  201. How do get data out?

  202. Outputs

  203. @samjulien

  204. @samjulien

  205. @samjulien

  206. @samjulien

  207. @samjulien

  208. customer-detail discount @samjulien

  209. customer-detail discount-ce @samjulien

  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
  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
  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
  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
  214. ng-on-* @samjulien

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

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

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

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

  219. How do we register multiple elements?

  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
  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
  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
  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
  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
  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
  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
  227. Tip: Group into Modules @samjulien

  228. Sharing Data with Services

  229. Here be dragons @samjulien

  230. Off-Roading with JS @samjulien

  231. Wrap Shared JS Services @samjulien

  232. Vanilla JS Service @samjulien

  233. Vanilla JS Service AngularJS Wrapper Angular Wrapper @samjulien

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

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

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

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

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

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

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

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

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

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

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

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

    = CustomerService.sharedInstance(); } } @samjulien
  246. customerService.getCustomers() @samjulien

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

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

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

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

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

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

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

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

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

  256. Inputs & Outputs @samjulien

  257. Indirection @samjulien

  258. , . @samjulien

  259. NgRx & Redux @samjulien

  260. Angular Elements @samjulien

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

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

  263. NgRx Large Apps High Complexity @samjulien

  264. Wrapping Up the Upgrade

  265. Add Component Selectors @samjulien

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

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

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

    ` }) export class CustomersTableComponent {}
  269. Switch Routing @samjulien

  270. Drop Elements @samjulien

  271. Drop Elements Clean up modules Uninstall Elements and tools Remove

    unused polyfills @samjulien
  272. Let’s Review

  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
  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
  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
  276. customElements.define( 'my-element', MyElement, { extends: 'p' } ); @samjulien

  277. None
  278. Angular Elements @samjulien

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

    vanilla JS services with wrappers Convert routing and remove Elements @samjulien
  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
  281. samj.im/ndc-london @samjulien

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