Web Components

 

It's all rainbows and unicorns! Is it?

Sara HARKOUSSE

UNITED DEV CONF

Minsk, 6,7 April 2017

http://vignette2.wikia.nocookie.net/weirdcommunity/images/a/a2/Rainbow-flow-abstract-backgrounds-for-powerpoint.jpg/revision/latest?cb=20130603085532​​

SARA HARKOUSSE

@Sara_harkousse

 

 

Tech Lead, front-end Web developer at Dassault Systèmes

3D design & PLM software

 

Electronics and Computer Science Engineer by training

 

 

About me

@UnitedDevConf #webcomponents

Women in Tech

Duchess France

@duchessfr

duchess-france.org

Elles bougent (Girls on The Move)

@ellesbougent

ellesbougent.com

@UnitedDevConf #webcomponents @Sara_harkousse

Web Components

 

It's all rainbows and unicorns! Is it?

http://vignette2.wikia.nocookie.net/weirdcommunity/images/a/a2/Rainbow-flow-abstract-backgrounds-for-powerpoint.jpg/revision/latest?cb=20130603085532​​

  • "The web components revolution"

  • "A Tectonic shift for web development"

  • "Web components are a game changer"

 

 

Web components'

public image

@UnitedDevConf #webcomponents @Sara_harkousse

Outline of this talk

@UnitedDevConf #webcomponents @Sara_harkousse

  • Motivation

  • Refresher on web components

  • Dive deep

  • Concluding remarks

Motivation

@UnitedDevConf #webcomponents @Sara_harkousse

  • Modular architecture

Filterable Product Table

Search Bar

Product Table

Product Row

Product Category Row

https://facebook.github.io/react/docs/thinking-in-react.html

@UnitedDevConf #webcomponents @Sara_harkousse

Angular 4.0 Component

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>`
})

export class AppComponent { name = 'Angular'; }
<my-app><h1>Hello Angular</h1></my-app>

https://embed.plnkr.co/?show=preview&show=app%2Fapp.component.ts

@UnitedDevConf #webcomponents @Sara_harkousse

Ember 2.12.0 Component

<article class="blog-post">
  <h1>{{title}}</h1>
  <p>{{yield}}</p>
  <p>Edit title: {{input type="text" value=title}}</p>
</article>
{{#each model as |post|}}
  {{#blog-post title=post.title}}
    {{post.body}}
  {{/blog-post}}
{{/each}}

https://guides.emberjs.com/v2.12.0/components/defining-a-component/

@UnitedDevConf #webcomponents @Sara_harkousse

Polymer 2.0 Element

  // Define the class for a new element called custom-element
  class CustomElement extends Polymer.Element {
    static get is() { return "custom-element"; }
    constructor() {
        super();
        this.textContent = "I'm a custom-element.";
      }
  }
  // Register the new element with the browser
  customElements.define(CustomElement.is, CustomElement);
<custom-element></custom-element>

http://plnkr.co/edit/PaCt2M?p=preview

@UnitedDevConf #webcomponents @Sara_harkousse

Frameworks make it hard to get along

@UnitedDevConf #webcomponents @Sara_harkousse

Harmony happens when there is less learning curve

@UnitedDevConf #webcomponents @Sara_harkousse

Browser support

https://www.webcomponents.org/

  • use webcomponents.js

Refresher on web components

@UnitedDevConf #webcomponents @Sara_harkousse

  • HTML Template Tag

  • HTML Imports

  • Custom Elements

  • Shadow DOM

HTML Template Tag

@UnitedDevConf #webcomponents @Sara_harkousse

  <template id="template">
    <div id="container">
      <img class="webcomponents" src="https://webcomponents/imgs/webcomponents-logo.svg">
    </div>
  </template>

https://jsfiddle.net/sara_harkousse/rnu8zc0c/

HTML Template Tag Usage

@UnitedDevConf #webcomponents @Sara_harkousse

var template = document.querySelector('#template');
var clone = document.importNode(template.content, true);
var host = document.querySelector('#host');
host.appendChild(clone);

https://jsfiddle.net/sara_harkousse/rnu8zc0c/

Template Tag drawbacks?

@UnitedDevConf #webcomponents @Sara_harkousse

HTML Template Tag

@UnitedDevConf #webcomponents @Sara_harkousse

  • A way to add inert DOM elements to your document

  • Not something that's going to revolutionize your apps

  • No two-way data binding, no binding at all

HTML Imports

@UnitedDevConf #webcomponents @Sara_harkousse

 <link rel="import" href="/imports/productRow.html"> 
 <script src="js/productRow.js"></script> 
 <template> 
    <!-- content-->
 <template> 

How to get my component?

@UnitedDevConf #webcomponents @Sara_harkousse

var link = document.querySelector('link[rel="import"]');
var content = link.import;

// Grab DOM from productRow.html's document.
var el = content.querySelector('.productRow');
document.body.appendChild(el.cloneNode(true));

Why not this way?

@UnitedDevConf #webcomponents @Sara_harkousse

var el = require('/imports/productRow');

@UnitedDevConf #webcomponents @Sara_harkousse

<!doctype html>
<html lang="en">

    <head>
        <link rel="import" href="/imports/productRow.html">
    </head>

    <body>
        <!-- index content -->
    </body>

</html>

Render blocking

@UnitedDevConf #webcomponents @Sara_harkousse

<!doctype html>
<html lang="en">

    <head>
        <link rel="import" href="/imports/productRow.html" async>
    </head>

    <body>
        <!-- index content -->
    </body>

</html>

async unblocks the renderer

@UnitedDevConf #webcomponents @Sara_harkousse

Hammering our server

  • Each one of the HTML imports is another XMLHttpRequest

@UnitedDevConf #webcomponents @Sara_harkousse

Build tools for HTML Import

  • e.g. Vulcanizr (turns all imports into one import)

@UnitedDevConf #webcomponents @Sara_harkousse

HTML Import requires a polyfill

  • ES6 module?

  • Note: polyfill doesn’t work with file:/// protocols because it does a hidden XMLHttpRequest.

https://www.webcomponents.org/

@UnitedDevConf #webcomponents @Sara_harkousse

Shadow DOM

  • A tiny document

  • exists inside of a host element

@UnitedDevConf #webcomponents @Sara_harkousse

Key concepts

  • Isolated DOM

  • Scoped CSS

  • Rendering of shadow tree replaces that of host tree

@UnitedDevConf #webcomponents @Sara_harkousse

Number of shadow trees

  • How many shadow trees are too many?

@UnitedDevConf #webcomponents @Sara_harkousse

What's inside the Shadow DOM?

  • What to put in the Shadow DOM and what not to?

 

 

<my-app>

@UnitedDevConf #webcomponents @Sara_harkousse

Shadow DOM requires a polyfill

  • Hard to polyfill

@UnitedDevConf #webcomponents @Sara_harkousse

Use/Don't use

Shadow DOM?

callback() {
    // Use it
    this.root = this.attachShadow({mode: 'open'});

    var template = document.querySelector('#template');
    var clone = document.importNode(template.content, true);
    this.root.appendChild(clone);
}

callback() {
    // Don't Use it
    this.root = this;

    var template = document.querySelector('#template');
    var clone = document.importNode(template.content, true);
    this.root.appendChild(clone);
}

@UnitedDevConf #webcomponents @Sara_harkousse

Use/Don't use

Shadow DOM?

/* Use it */
:host {
    color: red;
}

/* Don't use it */
custom-component {
    color: red;
}

@UnitedDevConf #webcomponents @Sara_harkousse

Use AND Don't use

Shadow DOM

if (shadowflag){
    this.root = this.attachShadow({mode: 'open'});
} else {
    this.root = this;
}
custom-component, :host {
    color: red;
}

@UnitedDevConf #webcomponents @Sara_harkousse

Custom Elements

class UnitedDevConButton extends HTMLElement {...}
window.customElements.define('unitedDevCon-button', UnitedDevConButton);
<unitedDevCon-button></unitedDevCon-button>
  • Acts like a div

@UnitedDevConf #webcomponents @Sara_harkousse

Custom Elements

class UnitedDevConButton extends HTMLButtonElement {...}
window.customElements.define('unitedDevCon-button', UnitedDevConButton, {extends: 'button'});
<button is="unitedDevCon-button" disabled>My button!</button>
  • Acts like a real button

@UnitedDevConf #webcomponents @Sara_harkousse

Custom Elements Namespace

  • Dash (-) rule

@UnitedDevConf #webcomponents @Sara_harkousse

Custom Elements ES6 Class

class UnitedDevCon-component extends HTMLElement {
  constructor() {
    super(); // always call super() first in the ctor.
    ...
  }
  connectedCallback() {
    ...
  }
  disconnectedCallback() {
    ...
  }
  attributeChangedCallback(attrName, oldVal, newVal) {
    ...
  }

}

@UnitedDevConf #webcomponents @Sara_harkousse

Component example

 Bootstrap Progress Bar

<div class="progress">
  <div class="progress-bar" style="width: 60%;">
    60%
  </div>
</div>

@UnitedDevConf #webcomponents @Sara_harkousse

Component example

class ProgressBar extends HTMLElement { 

    constructor() { 
        super(); // always call super() first in the ctor.
	this.shadow = this.attachShadow({mode: 'open'});
	this._complete = 0;				
    }

    get complete() {
        return this._complete;
    }

    set complete(val) {
        this.setAttribute('complete', val);
    }	
}

https://jsfiddle.net/sara_harkousse/kwwe75yy/

@UnitedDevConf #webcomponents @Sara_harkousse

class ProgressBar extends HTMLElement { 
	
    static get observedAttributes() {
        return ['complete'];
    }	

    attributeChangedCallback(name, oldval, newval) {
        // code
    }	
}

https://jsfiddle.net/sara_harkousse/kwwe75yy/

Component example

@UnitedDevConf #webcomponents @Sara_harkousse

class ProgressBar extends HTMLElement { 

    attributeChangedCallback(name, oldval, newval) {
        var innerBar = this.shadow.querySelector('.progress-bar-inner');
	switch(name) {
		case 'complete':
			this._complete =  parseInt(newval, 10) || 0;
			innerBar.style.width = this.complete + '%';
			innerBar.innerHTML = this.complete + '%';
	}
    }	
}

https://jsfiddle.net/sara_harkousse/kwwe75yy/

Component example

@UnitedDevConf #webcomponents @Sara_harkousse

class ProgressBar extends HTMLElement { 
    connectedCallback() {
        var template = `<style>
                        .progress-bar {
			    width: 50%;
			    ...
			}
			.progress-bar-inner {
			    height: 100%;
			    ....
			}
			</style>
			<div class="progress-bar">
				<div class="progress-bar-inner">${this.complete}%</div>
			</div>`; 
        this.shadow.innerHTML = template;
    }
}

https://jsfiddle.net/sara_harkousse/kwwe75yy/

Component example

@UnitedDevConf #webcomponents @Sara_harkousse

window.customElements.define('progress-bar', ProgressBar);
// Use
<progress-bar></progress-bar>

Component example

@UnitedDevConf #webcomponents @Sara_harkousse

Components on a page

<!doctype html>
<html lang="en">

    <head>
        <link rel="import" href="/imports/productRow.html">
    </head>

    <body>
        <custom-navbar style="compact"></custom-navbar>
        <custom-toolbar style="full"></custom-toolbar>
        <custom-pagecontent>
            <custom-item attr="val"></custom-item>
            <custom-input attr="val"></custom-input>
            <progress-bar></progress-bar>
        </custom-pagecontent>

        <script>
            document.querySelector('progress-bar').addEventListener(
                'customevent', function () {
                    // do something
                 }
            });
        </script>
    </body>

</html>

@UnitedDevConf #webcomponents @Sara_harkousse

General concerns

Should you load the polyfills in every bowser? 

No.

@UnitedDevConf #webcomponents @Sara_harkousse

var = webComponentsSupported = ('customElements' in window
        && !!HTMLElement.prototype.attachShadow
        && 'import' in document.createElement('link')
        &&  'content' in document.createElement('template'));


function loadScript(src) {
 return new Promise(function(resolve, reject) {
   const script = document.createElement('script');
   script.src = src;
   script.onload = resolve;
   script.onerror = reject;
   document.head.appendChild(script);
 });
}

// Lazy load the polyfill if necessary.
if (!supportsCustomElementsV1) {
  loadScript('webcomponents-lite.min.js').then(e => {
    // Polyfill loaded.
  });
} else {
  // Native support. Good to go.
}

@UnitedDevConf #webcomponents @Sara_harkousse

Complex documentation

  • not fun documents to read

@UnitedDevConf #webcomponents @Sara_harkousse

Takeaway

You will understand some of the design decisions of major frameworks if you learn about custom elements

@UnitedDevConf #webcomponents @Sara_harkousse

Takeaway

Who is using web components?

@UnitedDevConf #webcomponents @Sara_harkousse

Takeaway

GitHub

Mozilla VR

Google

@UnitedDevConf #webcomponents

Thanks for listening!

Follow for slides

@Sara_harkousse

Made with Slides.com