Slide 1

Slide 1 text

JS January 22nd, 2020 Rhein-Neckar #12 Web Components, ganz ohne Framework! Live & in Action @jahr_patrick [email protected]

Slide 2

Slide 2 text

Who am I? Patrick Jahr [email protected] @jahr_patrick Software Consultant und Developer @ Thinktecture AG https://www.thinktecture.com/patrick-jahr

Slide 3

Slide 3 text

• The challenge • Web Components • What are they? • How to use them? • How to create them? Agenda

Slide 4

Slide 4 text

The challenge

Slide 5

Slide 5 text

Components Components Components Components

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Slide 8

Slide 8 text

• No semantics • Changes are difficult • Framework-dependent

Slide 9

Slide 9 text

+ -
• Global scoping • Naming conflicts • Styling conflicts

Slide 10

Slide 10 text

• Semantics • Local scoping • Bundle import

Slide 11

Slide 11 text

Web Components

Slide 12

Slide 12 text

• Web Components are a collection of 3 technologies • Custom Elements • HTML Templates • Shadow DOM • Bring a native component model to web instead of having to use frameworks Web Components - What are they?

Slide 13

Slide 13 text

HTML Templates

Slide 14

Slide 14 text

Can I use - HTML templates?

Slide 15

Slide 15 text

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

Description

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

Slide 16

Slide 16 text

Demo… let´s start

Slide 17

Slide 17 text

Custom Elements

Slide 18

Slide 18 text

Can I use - Custom Elements?

Slide 19

Slide 19 text

• Create your own HTML tags • Lifecycle model (“connected”, “disconnected”, “attribute changed”, “adopted”) • Reusable • Decouple behavior from the document • ES6 Class • Inherit from HTMLElement or any other HTML element • Need to be defined in a CustomElementRegistry Custom Elements

Slide 20

Slide 20 text

otherElement.appendChild(myRating); otherElement.removeChild(myRating); document.body.appendChild(myRating); const myRating = document.createElement('my-rating') 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);

Slide 21

Slide 21 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 22

Slide 22 text

CustomElementRegistry

Slide 23

Slide 23 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 window.customElements.define('my-rating', MyRating);

Slide 24

Slide 24 text

Importing Web Components

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

Demo… back to

Slide 27

Slide 27 text

Shadow DOM

Slide 28

Slide 28 text

Can I use - Shadow DOM?

Slide 29

Slide 29 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 losses, no influence • 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 30

Slide 30 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 31

Slide 31 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 32

Slide 32 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 33

Slide 33 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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 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 36

Slide 36 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 37

Slide 37 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 38

Slide 38 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 any parts with that name, anywhere in the document • http://tabatkins.github.io/specs/css-shadow-parts/#part-theme Shadow DOM - CSS Selectors - Outside Shadow DOM

Slide 39

Slide 39 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-rating.styled::part(my-header) { background-color: black; color: white; }

Slide 40

Slide 40 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-rating::part(textspan) { color: red; } Innerspan Textspan

Slide 41

Slide 41 text

Demo… back to

Slide 42

Slide 42 text

Conclusion

Slide 43

Slide 43 text

Web Components help in giving a semantic structure in apps Leverage the shadow DOM to encapsulate your Web Components They encapsulate HTML and CSS Improve maintainability and reusability Smaller bundles with frameworks (Angular Ivy, Stencil, Lit-Element) Form Participation API Web Components - Summary

Slide 44

Slide 44 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: https://thinktecture.com/patrick-jahr • Repository: https://github.com/thinktecture/rnjs-2020-web-components • Contact: [email protected]

Slide 45

Slide 45 text

Take care! A sneak peek into the future

Slide 46

Slide 46 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 47

Slide 47 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 48

Slide 48 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 49

Slide 49 text

• Shadow DOM encapsulates the DOM only • 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 50

Slide 50 text

• Easy things may require more code • Attributes vs Properties • addEventListener etc. • Error handling • 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 51

Slide 51 text

• There is no built-in template engine, you may be used to from SPA frameworks like Angular, React, Vue etc. • JavaScript template strings are preferred for better readability • Can get complex and unreadable when conditions are used heavily No built-in template engine

Slide 52

Slide 52 text

No built-in template engine - Lit Element example get previewTemplate() { return html`
${this.preview.image ? html`Preview` : html`` }
${this.preview.title} ${this.preview.description ? html`

${this.preview.description}

` : html`` }
`; }

Slide 53

Slide 53 text

• Currently there is no auto completion support for Web Components • Stencil tries to solve this by generating TypeScript Definition files • VS Code & W3C are discussing possible solutions • https://github.com/w3c/webcomponents/issues/776 • Stencil is currently generating a possible solution with its compiler Editor support // type definitions

Slide 54

Slide 54 text

• There is no standard build process • You can use any tool you want, e.g. webpack, parcel, etc. • Be aware: • CSS post processing • Asset management Build process // custom asset management

Slide 55

Slide 55 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