Web Components

 

It's all rainbows and unicorns! Is it?

Sara HARKOUSSE

DevFest Nantes

19, 20 Octobre 2017

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

https://www.youtube.com/watch?v=ic4e50BK_ik

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

#devfestnantes #webcomponents @Sara_harkousse

Available recording in English

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

#devfestnantes #webcomponents

Women in Tech

Duchess France

@duchessfr

duchess-france.org

Elles bougent (Girls on The Move)

@ellesbougent

ellesbougent.com

#devfestnantes #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

#devfestnantes #webcomponents @Sara_harkousse

Web components'

public image

#devfestnantes #webcomponents @Sara_harkousse

  • "The broken promise of Web Components"

 

 

Outline of this talk

#devfestnantes #webcomponents @Sara_harkousse

  • Motivation

  • Refresher on web components

  • Dive deep

  • Concluding remarks

Motivation

#devfestnantes #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

#devfestnantes #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://angular.io/tutorial/toh-pt1#show-the-hero

#devfestnantes #webcomponents @Sara_harkousse

Ember 2.16.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.16.0/components/defining-a-component/

#devfestnantes #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

#devfestnantes #webcomponents @Sara_harkousse

Frameworks make it hard to get along

#devfestnantes #webcomponents @Sara_harkousse

Harmony happens when there is less learning curve

Browser support

https://www.webcomponents.org/

  • use webcomponents.js

#devfestnantes #webcomponents @Sara_harkousse

Refresher on web components

Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web apps.”

https://www.webcomponents.org/

#devfestnantes #webcomponents @Sara_harkousse

Refresher on web components

#devfestnantes #webcomponents @Sara_harkousse

  • HTML Template Tag

  • Shadow DOM

  • Custom Elements

  • HTML Imports

HTML Template Tag

#devfestnantes #webcomponents @Sara_harkousse

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

https://github.com/SaraHarkousse/Web_Components/tree/master/VanillaJS/Template

HTML Template Tag Usage

#devfestnantes #webcomponents @Sara_harkousse

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

https://github.com/SaraHarkousse/Web_Components/tree/master/VanillaJS/Template

HTML Template Tag

#devfestnantes #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

#devfestnantes #webcomponents @Sara_harkousse

Shadow DOM

  • A tiny document

  • exists inside of a host element

#devfestnantes #webcomponents @Sara_harkousse

Key concepts

  • Isolated DOM

  • Scoped CSS

#devfestnantes #webcomponents @Sara_harkousse

Shadow DOM requires a polyfill

  • Hard to polyfill

#devfestnantes #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);
}

#devfestnantes #webcomponents @Sara_harkousse

Use/Don't use

Shadow DOM?

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

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

#devfestnantes #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;
}

#devfestnantes #webcomponents @Sara_harkousse

Custom Elements

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

#devfestnantes #webcomponents @Sara_harkousse

Custom Elements

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

#devfestnantes #webcomponents @Sara_harkousse

Custom Elements Namespace

  • Dash (-) rule

#devfestnantes #webcomponents @Sara_harkousse

Custom Elements ES6 Class

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

}

HTML Imports

#devfestnantes #webcomponents @Sara_harkousse

 <link rel="import" href="component.html"> 
 <link rel="stylesheet" href="style.css"> 
 <div id="container">
     <img class="rotate" src="logo.svg">
 </div>
 <script src="script.js"></script>

[Deprecation] Styling master document from stylesheets defined in HTML Imports is deprecated, and is planned to be removed in M65, around March 2018. Please refer to https://goo.gl/EGXzpw for possible migration paths.

How to get my component?

#devfestnantes #webcomponents @Sara_harkousse

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

// Grab DOM from component.html's document.
var el = content.querySelector('#container');
var clone = el.cloneNode(true);
var host = document.querySelector('#host');
host.appendChild(clone);

#devfestnantes #webcomponents @Sara_harkousse

Hammering our server

  • Each one of the HTML imports is another XMLHttpRequest

#devfestnantes #webcomponents @Sara_harkousse

Build tools for HTML Import

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

#devfestnantes #webcomponents @Sara_harkousse

HTML Import requires a polyfill

  • ES module?

https://www.webcomponents.org/

#devfestnantes #webcomponents @Sara_harkousse

ES module

// export feature
export const Component = // … 

// import feature
import {Component} from '../components/cutom-component.js';

#devfestnantes #webcomponents @Sara_harkousse

Component example

 Bootstrap Progress Bar

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

#devfestnantes #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/

#devfestnantes #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

#devfestnantes #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

#devfestnantes #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

#devfestnantes #webcomponents @Sara_harkousse

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

Component example

#devfestnantes #webcomponents @Sara_harkousse

Components on a page

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

    <head>
        <link rel="import" href="/components/CustomNavbar.html">
        ..
    </head>

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

        <script>
            document.querySelector('custom-input').dispatchEvent(new CustomEvent('customevent', {
                detail: { prop: true }
            }));

            customElements.whenDefined('progress-bar').then(() => {
                document.querySelector('progress-bar').addEventListener(
                    'customevent', function () {
                        // do something
                     }
                });
            });

        </script>
    </body>

</html>

#devfestnantes #webcomponents @Sara_harkousse

Takeaway

  • Template tag? yeah! why not?

  • Use ES module instead of HTML Imports

  • Use Shadom DOM with a flag

  • Custom Elements?? Go, Go, Go

 

#devfestnantes #webcomponents @Sara_harkousse

Takeaway

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

#devfestnantes #webcomponents

Thanks for listening!

Follow for slides

@Sara_harkousse

https://github.com/SaraHarkousse

Made with Slides.com