Slide 1

Slide 1 text

Web Components Native Komponenten für’s Web - das Ende der SPA Frameworks? Manuel Rauber @ManuelRauber Consultant @ Thinktecture AG

Slide 2

Slide 2 text

Special Day “Modern Business Applications” Thema Sprecher Datum, Uhrzeit Raum Pragmatische Microservices mit .NET Core 3 – mehr als nur gRPC Christian Weyer DI, 25. Februar 2020, 10.30 bis 11.30 Ballsaal 2 Web Components: native Komponenten fürs Web – das Ende der SPA Frameworks? Manuel Rauber DI, 25. Februar 2020, 11.45 bis 12.45 Ballsaal 2 Progressive Web Apps – die Grundlagen Sebastian Springer DI, 25. Februar 2020, 14.15 bis 15.15 Ballsaal 2 Progressive Web-Apps mit Angular: Tipps für Fortgeschrittene Christian Liebel DI, 25. Februar 2020, 15.30 bis 16.30 Ballsaal 2 Blazor: SPAs im Browser mit C# und WebAssembly Christian Weyer DI, 25. Februar 2020, 17.15 bis 18.15 Ballsaal 2

Slide 3

Slide 3 text

Manuel Rauber Consultant @ Thinktecture AG [email protected] @manuelrauber https://manuel-rauber.com Microsoft MVP Who Am I?

Slide 4

Slide 4 text

• The challenge • Web Components • What are they? • How to use them? • How to create them? • Take Care & a peek into the future • What about frameworks?! Agenda

Slide 5

Slide 5 text

The challenge

Slide 6

Slide 6 text

The challenge https://dribbble.com/shots/7169781-Homestay-Web-Design https://dribbble.com/shots/7164322-Food-delivery-Landing-page-design

Slide 7

Slide 7 text

DRY

Slide 8

Slide 8 text

Components Components Components Components

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

!

Slide 11

Slide 11 text

!
! ! ! ! • No semantic • Intransparent • Changes are difficult • Framework dependent

Slide 12

Slide 12 text

! ! ! ! ! +! -! !
• Global Scoping • Naming conflicts • Styling conflicts

Slide 13

Slide 13 text

! ! ! ! ! • Semantic • Local Scoping • Configuration • Bundle Import

Slide 14

Slide 14 text

Web Components

Slide 15

Slide 15 text

• Web Components are not a standard, but a collection of 3 technologies • Custom Elements • HTML templates • Shadow DOM • (HTML Imports) • Bring a native component model to web instead of having to use frameworks Web Components - What are they?

Slide 16

Slide 16 text

Can I use …? IE11 Edge Chrome Android Opera Safari iOS Custom Elements ❌ ✅ ✅ ✅ ✅ ⚠ ⚠ Shadow DOM ❌ ✅ ✅ ✅ ✅ ⚠ ⚠ HTML Templates ❌ ✅ ✅ ✅ ✅ ✅ ✅

Slide 17

Slide 17 text

Custom Elements

Slide 18

Slide 18 text

• Create your own HTML tags • Lifecycle model (“connected”, “disconnected”, “attribute changed”, “adopted”) • Reusable • Decouple behavior from the document Custom Elements !

Slide 19

Slide 19 text

• ES6 Class • Inherit from HTMLElement or any other HTML element • Need to be defined in a CustomElementRegistry Custom Elements class MyRating extends HTMLElement { constructor() { super(); console.log('component constructed'); } connectedCallback() { console.log('component added to DOM'); } adoptedCallback() { console.log('component was moved in DOM'); } disconnectedCallback() { console.log('component removed from DOM'); } attributeChangedCallback(name, oldVal, newVal) { console.log('a attribute has been changed'); } } window.customElements.define('my-rating', MyRating); const myRating = document.createElement('my-rating'); document.body.appendChild(myRating); !// this moves the node! otherElement.appendChild(myRating); otherElement.removeChild(myRating);

Slide 20

Slide 20 text

• Static getter observedAttributes returns all attributes which should be observed • All those attributes will trigger attributeChangedCallback upon a change • Does not execute on property change! Custom Elements - Observed Attributes class MyRating extends HTMLElement { constructor() { super(); } static get observedAttributes() { return [ 'value' ]; } attributeChangedCallback(name, oldVal, newVal) { if (name !!=== 'value') { this.innerHTML = `Rating value ${newVal}`; } } } !

Slide 21

Slide 21 text

HTML Templates

Slide 22

Slide 22 text

• HTML tag: • Parsed, but not rendered • Instantiated via JavaScript HTML Templates My Image Card!

Description!

! const template = document.querySelector('template'); const templateInstance = document.importNode(template.content, true); const img = templateInstance.querySelector('img'); img.setAttribute('src', 'http:!//a-link.com/foo.png'); document.body.appendChild(templateInstance);

Slide 23

Slide 23 text

Shadow DOM

Slide 24

Slide 24 text

• Encapsulates HTML and CSS • Isolated DOM: elements within Shadow DOM are not selectable from outside • e.g. document.querySelector() won’t return a result • Scoped CSS: CSS styles stay within Shadow DOM, no leakage, no bleed-in • Composition: declarative, markup-based API, put elements from outside in • Simplifies CSS: no conflicts with existing classes and IDs • Productivity: app consists of chunks of components instead of one big page Shadow DOM https://developers.google.com/web/fundamentals/web-components/shadowdom

Slide 25

Slide 25 text

• Shadow host DOM node that the shadow DOM is attached to • Shadow tree DOM tree inside shadow DOM • Shadow boundary Place where the shadow DOM ends and the regular DOM begins (“Light DOM”) • Shadow root Root node of the shadow tree Shadow DOM document shadow host Document Tree Shadow Tree shadow root Shadow boundary

Slide 26

Slide 26 text

• attachShadow() attaches a shadow DOM to any HTML element • open and closed mode Shadow DOM !
!
const shadowHost = document.querySelector('div'); const shadowRoot = shadowHost.attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '<h1>Hello World!!</h1>'; ! ! !

Slide 27

Slide 27 text

• open allows access to the HTML element’s shadow DOM • closed does not allow access to the HTML element’s shadow DOM Shadow DOM !// !!... shadowHost.attachShadow({ mode: 'open' }); document.querySelector('div').shadowRoot.querySelector('h1'); !// !-> [HTMLHeadingElement] !// !!... shadowHost.attachShadow({ mode: 'closed '}); document.querySelector('div').shadowRoot.querySelector('h1'); !// !-> Can not read 'querySelector' of null

Slide 28

Slide 28 text

• A (named) placeholder to fill with your own markup • Basically mixes together two DOM trees: the shadow & light tree Shadow DOM - Slots :host { border: 1px solid black; display: block; } !
! With Default Content! !
! ! !!!
Unnamed Slot Content!
!
Unnamed Slot Content 2!
Another Slot Content!
!

Slide 29

Slide 29 text

• Slots emit an event, whenever its content changes: slotchange • Get the slotted elements via assignedNodes() • https://drafts.csswg.org/css-scoping/#slotted-pseudo Shadow DOM - Slots const slot = this.shadowRoot.querySelector('slot'); slot.addEventListener('slotchange', () !=> { const assignedNodes = slot.assignedNodes(); !// Do something with assignedNodes });

Slide 30

Slide 30 text

• Styling is done via -tags • All stylings are local and do not overwrite styles from other shadow DOMs • No other stylings will bleed into the shadow DOM Shadow DOM - Styling <template> <style> h1 { color: red; } !

Hello World!!

! const shadowRoot = document.querySelector('div') .attachShadow({ mode: 'open' }); shadowRoot.innerHTML = '

Hello World!!

' + 'h1 { color: red; }!';

Slide 31

Slide 31 text

• Inside shadow DOM • :host, :host(), :host-context(), ::slotted() • Outside shadow DOM • CSS Shadow Parts: ::part() • In Discussion: ::theme() • Deprecated: >>> (“shadow piercing”) Shadow DOM - CSS Selectors

Slide 32

Slide 32 text

• :host: Selects the shadow host element • :host(): Selects the shadow host element only, if it has a certain class • :host-context(): Selects the shadow host element only, if the selector given as the function's parameter matches the shadow host's ancestor(s) in the place it sits inside the DOM hierarchy • "::slotted(): Selects a slotted element if it matches the selector Shadow DOM - CSS Selectors - Inside Shadow DOM document shadow host Document Tree Shadow Tree shadow root Shadow boundary
!
!
!
!
!
!::slotted(div) {} ! ! !

Slide 33

Slide 33 text

Shadow DOM - CSS Selectors - Inside Shadow DOM :host { display: block; background-color: blue; } :host(.red) { background-color: red; } :host-context(main) { background-color: yellow; } !::slotted(article) { background-color: black; } !

Before slot!

Default Slot content!

After slot!

!
! ! ! ! ! Article Content ! !

Slide 34

Slide 34 text

• CSS Shadow Parts (Working Draft): "::part() • Allows to selectively expose elements from the shadow tree to the outside page for styling purposes • https://www.w3.org/TR/css-shadow-parts-1/ • CSS Shadow Parts (Unofficial Draft): "::theme() • Matches all given parts, no matter how deeply nested they are • http://tabatkins.github.io/specs/css-shadow-parts/#part-theme Shadow DOM - CSS Selectors - Outside Shadow DOM

Slide 35

Slide 35 text

• Allows to selectively expose elements from the shadow tree to the outside page for styling purposes Shadow DOM - CSS Selectors - Outside Shadow DOM - ::part header { background-color: crimson; } !
Style me! !
! my-element.styled!::part(my-header) { background-color: black; color: white; } ! ! !

Slide 36

Slide 36 text

• If a nested Web Component will expose an inner Web Component’s part, it has to use exportparts=“part1 part2 …” instead of part=“name” Shadow DOM - CSS Selectors - Outside Shadow DOM - ::part my-element!::part(textspan) { color: red; } ! Innerspan! Textspan! ! !

Slide 37

Slide 37 text

Importing Web Components

Slide 38

Slide 38 text

• ES6 Import • Via Code • Deprecated: HTML Import Importing Web Components ! !// static import import './my-element.js'; !// dynamic import import('./my-element.js') .then(!!...); !// or lazy-loading via const script = document.createElement('script'); script.src = './my-element.js'; document.body.appendChild(script); const myElement = document.createElement('my-element'); document.body.appendChild(myElement);

Slide 39

Slide 39 text

CustomElementRegistry

Slide 40

Slide 40 text

• Window global registry for all custom elements • API • define(): defines a new element • get(): returns the constructor of an element (or undefined) • whenDefined(): returns a promise which is fulfilled, when the custom element is available • upgrade(): upgrades an element before it is connected to its shadow root CustomElementRegistry

Slide 41

Slide 41 text

Take Care! A sneak peek into the future

Slide 42

Slide 42 text

Versioning My library A My library B 3rd party library 1.0 3rd party library 2.0 Application defines defines Uncaught DOMException: Failed to execute ‘define’ on ‘CustomElementRegistry’: the name “my-web- component” has already been used with this registry.

Slide 43

Slide 43 text

• Problem: Custom Element Registry is a window global object • Possible solution currently discussed by W3C • Scoped Custom Element Registry • https://github.com/w3c/webcomponents/issues/716 • Current usable solutions • Version your HTML tags • The host application is the only one loading WCs, even 3rd-party, but this could break other Web Components expecting a certain version Versioning

Slide 44

Slide 44 text

• If you wish to sync them, then you’ll need to sync them! • “Reflecting properties to attributes” or “Reflected DOM Attributes” • Helps to keep DOM representation in sync with its JavaScript state • Watch out! Properties can hold objects, numbers etc. Attributes only strings • Attributes vs Properties input = document.createElement('input'); console.log(input.getAttribute('value')); !// null input.value = 'one'; console.log(input.getAttribute('value')); !// null input.setAttribute('value', 'two'); console.log(input.value); !// one

Slide 45

Slide 45 text

• Shadow DOM encapsulates the DOM only • JavaScript is still global scoped • Use common techniques for scoping JavaScript • May use the global scope to your advantage for app-wide configurations • But global polyfills can cause problems (e.g. if you load zone.js twice) JavaScript is still global scoped

Slide 46

Slide 46 text

• Easy things may require more code • Attributes vs Properties • addEventListener etc. • Bigger component libraries need a strong foundation • Think of when and how to render your component • There is no virtual DOM like in React or Angular • You may need to think of partial DOM updates for bigger components Boilerplate & render loops

Slide 47

Slide 47 text

• Forms and Web Components don’t play well yet • FormData is not populated • does not work • Form Participation API aka “Form Attached Controls” is discussion • https://github.com/w3c/webcomponents/issues/815 • https://github.com/w3c/webcomponents/issues/814 • https://github.com/github/image-crop-element/pull/15 • https://github.com/axa-ch/patterns-library/issues/1258 Form Participation API

Slide 48

Slide 48 text

• Already good possibilities to create framework-independent components • Decide between view-only and data-controlling Web Components • Keep in mind, what is not working yet, but will come in the future • Leverage the shadow DOM to encapsulate your Web Components • Think of your Web Components API! Web Components - Summary

Slide 49

Slide 49 text

What about frameworks?!

Slide 50

Slide 50 text

• Web Components are an uprising good alternative to build frontends without frameworks involved • But! A lot of stuff is done easier with frameworks, e.g. Form Validation, State Handling, Data Management, CSS post processing • Why not wrap framework components into Web Components? • Angular: Angular Elements, @angular/elements • React: Wrap manually or use a community wrapper • Vue: Vue Web Components, @vue/web-components What about frameworks?!

Slide 51

Slide 51 text

DEMO

Slide 52

Slide 52 text

Conclusion

Slide 53

Slide 53 text

• Web Components help in giving a semantic structure in apps • Improves maintainability and reusability • Take care for stuff not working yet (e.g. Form Participation API) • SPA Frameworks can help in building Web Components • … with the odd of having bigger bundles • Web Components will make you think more about your APIs! Web Components - Summary

Slide 54

Slide 54 text

Web Components Article Series https://thinktecture.com/newsletter COMING SOON!

Slide 55

Slide 55 text

And… now? • Try it out! Fiddle around with Web Components • Use them! Include first small Web Components into your application • Be prepared! Keep up to date with the development of Web Components • Slides & Repo: https://thinktecture.com/manuel-rauber • Contact: [email protected] - @manuelrauber