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

Web Components: It’s all rainbows and unicorns! Is it?

By sara_harkousse

Web Components: It’s all rainbows and unicorns! Is it?

Web components are a tale of four w3c specifications. They are a hot topic now. We’ve all seen big headlines, for instance, “The Web Components revolution”, “Web Components are a game changer”, “A Tectonic Shift for Web Development”, … and so many others. They are certainly exciting and promising, nevertheless, there are some factors holding them back such as performance issues and lack of browser support. Some features seems to be more hassle than they’re worth. In this talk you’ll examine web components from a pragmatic stand point. So if you want to start using web components in production, come to learn what features can you use today. Actually, despite the still short browser support, some of web components features seems to be the best choice to start with . The assessment you’ll learn is the reflection of my personal research and work on my spare time and also feedbacks from my co-workers.

  • 687