Slide 1

Slide 1 text

@ManfredSteyer Web Components with Angular: Beyond the basics Manfred Steyer SOFTWAREarchitekt.at ManfredSteyer

Slide 2

Slide 2 text

@ManfredSteyer About me … • Manfred Steyer SOFTWAREarchitekt.at • Angular Trainings and Consultancy • Google Developer Expert (GDE) • Angular Trusted Collaborator Page ▪ 2 Manfred Steyer Public: Vienna, Munich, Frankfurt In-House: Everywhere in Europe http://www.softwarearchitekt.at/workshops

Slide 3

Slide 3 text

@ManfredSteyer Contents • Motivation & Angular Elements 101 • Polyfills • Dynamic & Lazy elements • External elements • Dealing with dependencies • Excluding Zone.js

Slide 4

Slide 4 text

@ManfredSteyer Motivation

Slide 5

Slide 5 text

@ManfredSteyer Web Components: Framework independent components

Slide 6

Slide 6 text

@ManfredSteyer Standards Templates HTML Imports Custom Elements Shadow DOM

Slide 7

Slide 7 text

@ManfredSteyer Some Possibilities … Widget Libraries CMS Integration Enriching existing Apps Migrating Legacy Apps

Slide 8

Slide 8 text

@ManfredSteyer Angular Elements

Slide 9

Slide 9 text

@ManfredSteyer Angular Elements Wrap Angular Components Expose Custom Elements

Slide 10

Slide 10 text

@ManfredSteyer Angular Elements @NgModule({ imports: [BrowserModule], declarations: [DateComponent], entryComponents: [DateComponent] }) export class AppModule { }

Slide 11

Slide 11 text

@ManfredSteyer Angular Elements @NgModule({ imports: [BrowserModule], declarations: [DateComponent], entryComponents: [DateComponent] }) export class AppModule { }

Slide 12

Slide 12 text

@ManfredSteyer Angular Elements @NgModule({ imports: [BrowserModule], declarations: [DateComponent], entryComponents: [DateComponent] }) export class AppModule { constructor(private injector: Injector) { } }

Slide 13

Slide 13 text

@ManfredSteyer Angular Elements @NgModule({ imports: [BrowserModule], declarations: [DateComponent], bootstrap: [], entryComponents: [DateComponent] }) export class AppModule { constructor(private injector: Injector) { const DateElement = createCustomElement( DateComponent, { injector: this.injector }); customElements.define('date-picker', DateElement); } }

Slide 14

Slide 14 text

@ManfredSteyer CUSTOM_ELEMENTS_SCHEMA @NgModule({ […], schemas: [ CUSTOM_ELEMENTS_SCHEMA ] }) export class MyModule {}

Slide 15

Slide 15 text

@ManfredSteyer DEMO

Slide 16

Slide 16 text

@ManfredSteyer Polyfills

Slide 17

Slide 17 text

@ManfredSteyer Distribution

Slide 18

Slide 18 text

@ManfredSteyer Polyfill

Slide 19

Slide 19 text

@ManfredSteyer Selected Polyfills • webcomponents-loader.js • Loads Polyfills on demand • Custom Elements • Shadom DOM • Templates • native-shim.js • Needed for browsers that DO support polyfills … • … as long a we downlevel to ES5

Slide 20

Slide 20 text

@ManfredSteyer DEMO

Slide 21

Slide 21 text

@ManfredSteyer Dynamic and Lazy Elements

Slide 22

Slide 22 text

@ManfredSteyer Adding Custom Elements dynamically const tile = document.createElement('dashboard-tile');

Slide 23

Slide 23 text

@ManfredSteyer Adding Custom Elements dynamically const tile = document.createElement('dashboard-tile‘); tile['prop'] = 123; tile.setAttribute('class', 'col-lg-4 col-md-3 col-sm-2') tile.addEventListener([…])

Slide 24

Slide 24 text

@ManfredSteyer Adding Custom Elements dynamically const tile = document.createElement('dashboard-tile‘); tile['prop'] = 123; tile.setAttribute('class', 'col-lg-4 col-md-3 col-sm-2') tile.addEventListener([…]) content.appendChild(tile);

Slide 25

Slide 25 text

@ManfredSteyer Lazy Loading Register module in angular.json Load with with NgModuleFactoryLoader

Slide 26

Slide 26 text

@ManfredSteyer Register in angular.json "lazyModules": [ "[…]/lazy-dashboard-tile.module.ts" ],

Slide 27

Slide 27 text

@ManfredSteyer Register in angular.json @Injectable({ providedIn: 'root' }) export class LazyDashboardTileService { }

Slide 28

Slide 28 text

@ManfredSteyer Register in angular.json @Injectable({ providedIn: 'root' }) export class LazyDashboardTileService { constructor( private loader: NgModuleFactoryLoader, private injector: Injector ) {} }

Slide 29

Slide 29 text

@ManfredSteyer Register in angular.json @Injectable({ providedIn: 'root' }) export class LazyDashboardTileService { constructor( private loader: NgModuleFactoryLoader, private injector: Injector ) {} load(): Promise { return this.loader.load(path).then(moduleFactory => { }); } }

Slide 30

Slide 30 text

@ManfredSteyer Register in angular.json @Injectable({ providedIn: 'root' }) export class LazyDashboardTileService { constructor( private loader: NgModuleFactoryLoader, private injector: Injector ) {} load(): Promise { return this.loader.load(path).then(moduleFactory => { moduleFactory.create(this.injector); […] }); } }

Slide 31

Slide 31 text

@ManfredSteyer DEMO

Slide 32

Slide 32 text

@ManfredSteyer External Elements

Slide 33

Slide 33 text

@ManfredSteyer Project A Custom Element Custom Element Bundle Project B

Slide 34

Slide 34 text

@ManfredSteyer Register Custom Element when bootstrapping Compile application to self contained bundle Load bundle into consumer 3 Steps

Slide 35

Slide 35 text

@ManfredSteyer Register when bootstrapping @NgModule({ […] bootstrap: [] }) export class AppModule { }

Slide 36

Slide 36 text

@ManfredSteyer Register when bootstrapping @NgModule({ […] bootstrap: [] }) export class AppModule { constructor(private injector: Injector) { } ngDoBootstrap() { } }

Slide 37

Slide 37 text

@ManfredSteyer Register when bootstrapping @NgModule({ […] bootstrap: [], }) export class AppModule { constructor(private injector: Injector) { } ngDoBootstrap() { const externalTileCE = createCustomElement( ExternalDashboardTileComponent, { injector: this.injector }); customElements.define('external-dashboard-tile', externalTileCE); } }

Slide 38

Slide 38 text

@ManfredSteyer Compile to self-contained bundle

Slide 39

Slide 39 text

@ManfredSteyer ngx-build-plus: --single-bundle

Slide 40

Slide 40 text

@ManfredSteyer Load into consumer const script = document.createElement('script'); script.src = 'assets/external-dashboard-tile.bundle.js'; document.body.appendChild(script);

Slide 41

Slide 41 text

@ManfredSteyer DEMO

Slide 42

Slide 42 text

@ManfredSteyer Dealing with Dependencies

Slide 43

Slide 43 text

@ManfredSteyer Bundles Custom Element 1 Custom Element 2 Custom Element 3 Libraries: Angular, RxJS, … Libraries: Angular, RxJS, …

Slide 44

Slide 44 text

@ManfredSteyer Tree Shaking + Ivy (Angular 8.x) Custom Element 1 Custom Element 2 Custom Element 3

Slide 45

Slide 45 text

@ManfredSteyer

Slide 46

Slide 46 text

@ManfredSteyer Where Ivy can help ✓ Widgets based on @angular/core, @angular/common  Libs like @angular/forms or @angular/router

Slide 47

Slide 47 text

@ManfredSteyer Sharing Libs Custom Element 1 Custom Element 2 Custom Element 3 Libraries: Angular, RxJS, … UMD Drawbacks: Complexity, no isolation

Slide 48

Slide 48 text

@ManfredSteyer DEMO

Slide 49

Slide 49 text

@ManfredSteyer Excluding Zone.js

Slide 50

Slide 50 text

@ManfredSteyer How to exclude it? platformBrowserDynamic().bootstrapModule(AppModule, {ngZone: 'noop'})

Slide 51

Slide 51 text

@ManfredSteyer Consequences • Manual Change Detection • Change Detection via Observables

Slide 52

Slide 52 text

@ManfredSteyer DEMO

Slide 53

Slide 53 text

@ManfredSteyer Summary Polyfills Adding dynamically Lazy loading Compiling and loading external elements Dependencies Zone.js

Slide 54

Slide 54 text

@ManfredSteyer Contact and Downloads [mail] [email protected] [web] SOFTWAREarchitekt.at [twitter] ManfredSteyer d Slides & Examples Public and In-House http://www.softwarearchitekt.at/workshops