Slide 1

Slide 1 text

Angular Revisited Tree-shakable Components and Optional NgModules

Slide 2

Slide 2 text

I think NgModule is something that if we didn’t have to, we wouldn’t introduce. Back in the day, there was a real need for it. With Ivy and other changes to Angular over the years, we are working towards making those optional. — Igor Minar at AngularConnect 2018

Slide 3

Slide 3 text

The way NgModule works tends to be confusing to new Angular developers. Even if we weren’t to rip it out completely, we would change how it works and simplify things. — Alex Rickabaugh at AngularConnect 2018

Slide 4

Slide 4 text

Ivy out-of-the-box • Faster builds • Better debugging • Smaller bundle size • Instruction set similar to Incremental DOM

Slide 5

Slide 5 text

Ivy is well-suited for • Micro front-ends • Angular Elements • Web apps where Angular is not in control of the entire document

Slide 6

Slide 6 text

Zoneless change detection with Ivy 1. Change local UI state. 2. Mark component for check. 3. Schedule change detection cycle. @Component({ selector: 'zippy', template: `{{title}}
`, }) export class ZippyComponent { @Input() title: string; isExpanded = false; onToggle() { this.isExpanded = !this.isExpanded; markDirty(this); } } ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 7

Slide 7 text

We use Angular modules to • Configure injectors for runtime resolving of dependencies • Link declarables when compiling component templates • Bootstrap components as entry points to our applications • Lazy-load application features

Slide 8

Slide 8 text

Tree-shakable components are • Entry components but tree-shakable by the build process • Self-contained and independent from Angular modules • The smallest compilation and code splitting unit for Angular

Slide 9

Slide 9 text

import { Component, Input, ɵmarkDirty as markDirty } from '@angular/core'; import { ButtonDirective } from './button.directive'; @Component({ deps: [ ButtonDirective, ], selector: 'zippy', template: `{{title}}
`, }) export class ZippyComponent { @Input() title: string; isExpanded = false; onToggle() { this.isExpanded = !this.isExpanded; markDirty(this); } } Tree-shakable components with proposed component API ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 10

Slide 10 text

import { Component } from '@angular/core'; import { CapitalizePipe } from './capitalize.pipe'; import { ZippyComponent } from './zippy.component'; @Component({ deps: [ CapitalizePipe, ZippyComponent, ], template: `{{title | capitalize}}`, }) export class AppComponent { title = 'proposed component API'; } Tree-shakable components with proposed component API ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 11

Slide 11 text

Single component Angular modules • Declare and export only one component • Import only the declarables used by their specific component • Are useful for isolated component tests • Can be placed in the same file as their component • The abbreviated form is SCAM ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 12

Slide 12 text

import { Component, Input, NgModule } from '@angular/core'; import { ButtonModule } from './button.directive'; @Component({ selector: 'zippy', template: `{{title}}
`, }) export class ZippyComponent { @Input() title: string; isExpanded = false; onToggle() { this.isExpanded = !this.isExpanded; } } @NgModule({ declarations: [ZippyComponent], exports: [ZippyComponent], imports: [ButtonModule], }) export class ZippyModule {} Faux tree-shakable components with SCAMs ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 13

Slide 13 text

import { Component, NgModule } from '@angular/core'; import { CapitalizeModule } from './capitalize.pipe'; import { ZippyModule } from './zippy.component'; @Component({ template: `{{title | capitalize}}`, }) export class AppComponent { title = 'single component angular modules’; } @NgModule({ declarations: [AppComponent], imports: [ CapitalizeModule, ZippyModule, ], }) export class AppModule {} Faux tree-shakable components with SCAMs ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 14

Slide 14 text

Component render modules • Exclusively declare dependencies, no Angular module imports • Declare all the declarables used by their specific component and additionally the component itself • Can have duplicate declarations between Angular modules • Only work because of a timing issue ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 15

Slide 15 text

import { Component, Input, NgModule, ɵmarkDirty as markDirty } from '@angular/core'; import { ButtonDirective } from './button.directive'; @Component({ selector: 'zippy', template: `{{title}}
`, }) export class ZippyComponent { @Input() title: string; isExpanded = false; onToggle() { this.isExpanded = !this.isExpanded; markDirty(this); } } @NgModule({ declarations: [ ZippyComponent, ButtonDirective, ] }) export class ZippyRenderModule {} Component render modules (Ivy preview with JIT) ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 16

Slide 16 text

import { Component, NgModule } from '@angular/core'; import { CapitalizePipe } from './capitalize.pipe'; import { ZippyComponent } from './zippy.component'; @Component({ template: `{{title | capitalize}}`, }) export class AppComponent { title = 'component render modules’; } @NgModule({ declarations: [ AppComponent, CapitalizePipe, ZippyComponent, ], }) export class AppRenderModule {} Component render modules (Ivy preview with JIT) ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 17

Slide 17 text

Feature render modules • Every component exports a nested declarable array • A single feature render module per bundle • Angular will flatten the declarable arrays and remove duplicates ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 18

Slide 18 text

import { Component, Input, ɵmarkDirty as markDirty } from '@angular/core'; import { ButtonDirective } from './button.directive'; @Component({ selector: 'zippy', template: `{{title}}
`, }) export class ZippyComponent { @Input() title: string; isExpanded = false; onToggle() { this.isExpanded = !this.isExpanded; markDirty(this); } } export const zippyDeps = [ ZippyComponent, ButtonDirective, ]; Feature render modules (Ivy preview with AOT) ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 19

Slide 19 text

import { Component, NgModule } from '@angular/core'; import { CapitalizePipe } from './capitalize.pipe'; import { zippyDeps } from './zippy.component'; @Component({ template: `{{title | capitalize}}`, }) export class AppComponent { title = 'feature render modules’; } @NgModule({ declarations: [ AppComponent, CapitalizePipe, zippyDeps, ], }) export class AppRenderModule {} Feature render modules (Ivy preview with AOT) ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 20

Slide 20 text

We use Angular modules to • Configure injectors for runtime resolving of dependencies • Link declarables when compiling component templates • Bootstrap components as entry points to our applications • Lazy-load application features

Slide 21

Slide 21 text

Bootstrapping tree-shakable components Unlike View Engine: • No NgZone • No default change detection • No application initialisers • No application initialisation status • No application bootstrap hook import { ɵrenderComponent as renderComponent } from '@angular/core'; import { AppComponent } from './app.component'; renderComponent(AppComponent); ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 22

Slide 22 text

We use Angular modules to • Configure injectors for runtime resolving of dependencies • Link declarables when compiling component templates • Bootstrap components as entry points to our applications • Lazy-load application features

Slide 23

Slide 23 text

Lazy-loading tree-shakable components 1. Dynamically import the component file 2. Extract the component from the file 3. Bootstrap the component import { ɵrenderComponent as renderComponent } from '@angular/core'; import('./my-ivy.component').then(({ MyIvyComponent }) => renderComponent(MyIvyComponent); ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 24

Slide 24 text

This talk is possible thanks to ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES Alexey Zuev Joost Koehoorn Max Koretskyi Morten Kirstein Wassim Chegham Zejiang Yu

Slide 25

Slide 25 text

Tree-shakable components with Ivy • Get rid of all Angular modules • Bootstrap using renderComponent • Schedule change detection using markDirty when local UI state changes • Lazy-load application features using dynamic import and renderComponent ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

Slide 26

Slide 26 text

Techniques for getting started today ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES View Engine Ivy preview with JIT Ivy preview with AOT SCAMs ✓ ✓ ✓ Component render modules ✓ Feature render modules ✓ ✓ Proposed component API

Slide 27

Slide 27 text

Get in touch ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES bit.do/angular-revisited-slides bit.do/angular-revisited github.com/LayZeeDK/angular-revisited github.com/LayZeeDK twitter.com/LayZeeDK www.linkedin.com/in/larsgbn/ [email protected]