KEMBAR78
Web Components v1 | PDF
CLUB AJAX
by Mike Wilcox, January 2017
Web Components
and ES6 Classes
JS Frameworks
• Some more mobile friendly than others
• Although hard to say that any of them truly are
• Jeff Atwood complains about poor Ember perf for Discourse
• Isomorphic/Universal Apps using server side rendering
• Creates faster visual content on the page
• But also creates a “valley of death” where the page is no functional
• Virtual DOM can sometimes be effective
• Sometimes effective
• Sometimes not
• On demand loading
• Some frameworks support it, some don’t
Dojo
Dojo
<div class='createSection' data-dojo-attach-point='createSection'>

<div class='row'>

<div data-dojo-attach-point="filterName"

data-dojo-type="app/components/ui/TextFilter" 

data-dojo-props="fieldLabel:'${messages.filterName}', required:
true”>
Dojo
<div class='field'>

${labelNode}

<div data-dojo-attach-point="textbox" class='fieldWidget'

data-dojo-type="dijit/form/TextBox"

data-dojo-attach-event="onKeyUp: _update"></div>

</div>
define([

'dojo/_base/declare',

'dx/Widget',

'dojo/text!./templates/TextFilter.html',

'dijit/form/TextBox'

], function(declare, Widget, template) {



return declare(Widget, {

templateString: template,

postMixInProperties: function(){ },

postCreate: function(){ },

focus: function(){

this.textbox.focus();

},

_setValueAttr: function(value){

this.textbox.set('value', value);

},



_getValueAttr: function(){

return this.textbox.get('value');

},
Dojo
Ember
Ember
<div class="client-page">



{{side-nav nav=navigation clientId=model.client.id}}
Ember
<div class="nav-items">

{{#each nav as |link|}}

{{#unless (eq link.state "hidden") }}

{{#if (eq link.state "disabled") }}

<div class="side-nav-link {{link.state}}">

<div class="side-nav-title">{{link.title}}</div>

</div>

{{else}}

{{#link-to link.route title=link.title class=link.state}}

<div class="side-nav-link {{link.state}}">

<div class="side-nav-title">{{link.title}}</div>

</div>

{{/link-to}}

{{/if}}

{{/unless}}

{{/each}}

</div>
Ember
import Ember from ‘ember';
import Navigation from '../../mixins/navigation';


export default Ember.Component.extend(Navigation, {

classNames: ['side-nav'],

nav: null,
clientId: ‘NA’,
activeNavigation: Ember.computed('navigation', function () {

let activeNavigationObject = [];

this.get('navigation').forEach((mainItem)=> {

...

});

return activeNavigationObject;

}),


});
Angular 2
Angular
<div [ngBusy]="{busy: busy, message: 'Please wait...'}">

<div *ngIf="settingView">

<my-actions [links]="settingView.actionUrls"></my-actions>

</div>

</div>
Angular
<h4>Actions</h4>

<div class="section">

<a href="{{links[0]}}">Change Your Password</a>

<a href="{{links[1]}}">Change Your Security Questions</a>

<a href="{{links[2]}}">Change other usernames for a Single Sign-
On Experience</a>

</div>
Angular
import { Component, Input, OnInit } from '@angular/core';



@Component({

moduleId: module.id.toString(),

selector: 'my-actions',

templateUrl: 'actions.component.html'

})

export class ActionsComponent implements OnInit {

constructor() { }



@Input()

links: string[] = [];



ngOnInit() { }

}
React
React
import React from 'react';

import { render } from 'react-dom';

import Text from './form-sections/text';

export default class Form extends React.Component {

constructor(props) {

super(props);

}

render() {

return ( 

<ay-form ref="form">

<Text />

</ay-form> 

);

}

}

React
import React from 'react';

import { render } from 'react-dom';

import pure from 'react-addons-pure-render-mixin';



export default class Text extends React.Component {

constructor(props) {

super(props);

this.shouldComponentUpdate = pure.shouldComponentUpdate.bind(this);

}

render() {

return (

<section>

<ay-field type="text" value="" name="where"
placeholder="Where"></ay-field>

</section>

);

}

}
jQuery
React
<div class="jq-picker"></div>

<script>

$.ready(function(){

$(".jq-picker").jqPicker(options);

});

</script>
JS Frameworks + Web Components
• Web Components are “just HTML” so they will work with all of them
• It may require some manual wiring, like using addEventListener
• With plugins, you can make use of framework template functionality
• https://github.com/webcomponents/react-integration
• https://github.com/clubajax/react-inject-change-events
What are they?
Web Components Benefits
• Part of the DOM - lifecycle tools for free!
• Future-proof and cross-browser (web standard) for creating and extending
reusable components.
• Requires no library or framework to get started. Vanilla JS/HTML FTW!
• Provides a familiar programming model. It's just DOM/CSS/HTML.
• Creates a very modular system
• Tightly integrated with the browser's DevTools.
• Leverage existing accessibility features.
Web Components
• Custom Elements
• Shadow DOM
• Templates
• HTML Imports
All four of these items are a WC3 spec, all of which makes up Web Components
Custom Elements
• Define your own element types and functionality
• Provides a standard way to associate JavaScript logic with an
element
• Lifecycle methods
• No confusing context — “this” is the element
• Easy to inspect in debuggers
We could do custom elements with
IE6… the difference is the life cycle
methods
<x-tabs>

<ul>Tab 1</ul>

<ul>Tab 2</ul>

<ul>Tab 3</ul>

</x-tabs>
Shadow DOM
Content can be
“projected” into
provided slots
• Shadow DOM refers to the ability of the browser to include a subtree of
DOM elements into the rendering of a document, but not into the main
document DOM tree.
• CSS style rules are constrained to the shadow tree they were defined in.
• The DOM is also encapsulated. For example,
document.querySelectorAll('div'); will not return any results from a
custom element with Shadow DOM
Templates
• The template element is used to declare fragments of HTML
that can be cloned and inserted in the document by script.
• Contains a DocumentFragment in its
HTMLTemplateElement.content property
HTML Imports
<link rel=“import” href=“./my-bugger.html” />

<link rel=“import” href=“./my-zapper.html” />
• Can import all in one file: JavaScript, HTML, and CSS
• Effectively, an HTML document.
• HTML Imports can be the dependency management system, replacing
AMD or CommonJS
• HTML Imports let you include and reuse HTML documents in other HTML
documents, just as script tags let you include external JavaScript in their
pages.
HTML Imports - CONS
The HTML Import spec does not
have consensus. Mozilla is still
holding out, after two years.
<link rel=“import” href=“./my-bugger.html” />

<link rel=“import” href=“./my-zapper.html” />
• Confusing context when importing templates
• Spec essentially competes with ES modules
• Globals
Can I use…?
Browser Support
• Chrome v54
• WebKit Nightly v18
• Edge has begun prototyping
• Firefox has an open bug
Browser Stats
We need to support old IE because we…
• want to expose our servers to unsupported, insecure browsers.
• like to pay the extra IE development cost (8:25%, 9:10%, 10:5%).
• enjoy tying up QA to spend extra time testing more browser versions
https://www.xfive.co/blog/stop-supporting-ie10-ie9-ie8/
Seriously, management often encourages
the support of old versions because they
are not aware of the cost and risk.
Polyfills
• A polyfill for HTML Custom Elements.
• ShadyDOM provides a shim for ShadowDOM V1. It is less correct but less
intrusive and faster than the ShadowDOM Polyfill.
• ShadyCSS provides a shim for CSS Custom Properties, CSS Mixins with
@apply support, and ShadowDOM V1 style encapsulation with the ShadyDOM
library.
v0 vs v1*
*The differences between the old spec and the recent changes
v0
var proto = Object.create(HTMLElement.prototype);

proto.createdCallback = function() {

// invoked on creation

};

proto.attachedCallback = function() {

// invoked when added to the document

};

proto.detachedCallback = function() {

// invoked when removed from the document

};

proto.attributeChangedCallback = function(attrName, oldVal, newVal) {

// invoked when an attribute is changed

};


var MyComponent = document.registerElement('my-component', {

prototype: proto

});
class MyComponent extends HTMLElement {

static get observedAttributes() {
return ['value', 'disabled'];
}

constructor ( ) {

super();

}

connectedCallback ( ) { }

disconnectedCallback ( ) { }

attributeChangedCallback(attrName, oldVal, newVal) { }

adoptedCallback() { }

}

customElements.define('my-component', MyComponent);
v1
constructor: the element is
created or upgraded
connectedCallback: the
element is inserted into the DOM
disconnectedCallback: the
element is removed from the DOM.
attributeChangedCallback:
an attribute was added, removed,
or updated
adoptedCallback the element
has been moved into a new
document
define: exposes the element
name for use in markup
History
Q: Why did the spec change?
A: The old spec did not work with ES6 classes
Q: Does the new spec work wth ES5?
A: uh, sort of…
Q: Who changed it?
A: Blame Apple's Ryosuke Niwa. He’s made a few changes…
Q: Should I wait to use Web Components until v1 is implemented?
A: No, not any more than you would wait for any other shim-able browser feature
Inheritance
class MyButton extends HTMLButtonElement {

constructor () {

super(...arguments);

}

}

customElements.define('my-button', MyButton, {extends: 'button'});
Extends something other than
HTMLElement
Extra argument when defining
Blocked by Apple.
Use native tag, and extended
tag in “is”
This is what it would look like… if it were implemented.
ES6 Classes
class MyComponent extends HTMLElement {

static something () { return 'something'; }

constructor () {

super();

}

connectedCallback () {

//

}

}
always call super() first in the
constructor. “this” is not established until
after
Commas are forbidden to emphasize that
class definitions are different from object
literals.
Classes are sugar for prototypal inheritance. MyComponent is a function that
inherits from the HTMLElement prototype.
static is similar to:
MyComponent.something = function () {
return ’something’;
}
data properties are not allowed like in objects:
{a: true} (yet)
getters and setters must be used.
super is used as a function in the
constructor, as property references:
super.someMethod();
Custom Element API Considerations
• Sync important properties with attributes
• getters/setters over methods
• Broadcast changes via events
• Use standard properties and event names (“value”, “change”)
• innerHTML — what happens, before and after initialization?
• Appending children
• Use :defined or alternative
• Consider Shadow DOM styling pros and cons
DEMOS
Refs
https://github.com/webcomponents/shadydom
https://github.com/webcomponents/shadycss
https://webkit.org/blog/7027/introducing-custom-elements/
https://github.com/webcomponents/react-integration
https://github.com/clubajax/react-inject-change-events
https://hacks.mozilla.org/2014/12/mozilla-and-web-components/
https://www.polymer-project.org/1.0/blog/2016-12-21-polymer-2.0-update
https://developers.google.com/web/fundamentals/getting-started/primers/customelements?hl=en#historysupport
https://twitter.com/AaronGustafson/status/717028669948977153
https://github.com/w3c/webcomponents/issues/509
CLUB AJAX

Web Components v1

  • 1.
  • 2.
    by Mike Wilcox,January 2017 Web Components and ES6 Classes
  • 3.
    JS Frameworks • Somemore mobile friendly than others • Although hard to say that any of them truly are • Jeff Atwood complains about poor Ember perf for Discourse • Isomorphic/Universal Apps using server side rendering • Creates faster visual content on the page • But also creates a “valley of death” where the page is no functional • Virtual DOM can sometimes be effective • Sometimes effective • Sometimes not • On demand loading • Some frameworks support it, some don’t
  • 5.
  • 6.
    Dojo <div class='createSection' data-dojo-attach-point='createSection'>
 <divclass='row'>
 <div data-dojo-attach-point="filterName"
 data-dojo-type="app/components/ui/TextFilter" 
 data-dojo-props="fieldLabel:'${messages.filterName}', required: true”>
  • 7.
    Dojo <div class='field'>
 ${labelNode}
 <div data-dojo-attach-point="textbox"class='fieldWidget'
 data-dojo-type="dijit/form/TextBox"
 data-dojo-attach-event="onKeyUp: _update"></div>
 </div>
  • 8.
    define([
 'dojo/_base/declare',
 'dx/Widget',
 'dojo/text!./templates/TextFilter.html',
 'dijit/form/TextBox'
 ], function(declare, Widget,template) {
 
 return declare(Widget, {
 templateString: template,
 postMixInProperties: function(){ },
 postCreate: function(){ },
 focus: function(){
 this.textbox.focus();
 },
 _setValueAttr: function(value){
 this.textbox.set('value', value);
 },
 
 _getValueAttr: function(){
 return this.textbox.get('value');
 }, Dojo
  • 9.
  • 10.
  • 11.
    Ember <div class="nav-items">
 {{#each navas |link|}}
 {{#unless (eq link.state "hidden") }}
 {{#if (eq link.state "disabled") }}
 <div class="side-nav-link {{link.state}}">
 <div class="side-nav-title">{{link.title}}</div>
 </div>
 {{else}}
 {{#link-to link.route title=link.title class=link.state}}
 <div class="side-nav-link {{link.state}}">
 <div class="side-nav-title">{{link.title}}</div>
 </div>
 {{/link-to}}
 {{/if}}
 {{/unless}}
 {{/each}}
 </div>
  • 12.
    Ember import Ember from‘ember'; import Navigation from '../../mixins/navigation'; 
 export default Ember.Component.extend(Navigation, {
 classNames: ['side-nav'],
 nav: null, clientId: ‘NA’, activeNavigation: Ember.computed('navigation', function () {
 let activeNavigationObject = [];
 this.get('navigation').forEach((mainItem)=> {
 ...
 });
 return activeNavigationObject;
 }), 
 });
  • 13.
  • 14.
    Angular <div [ngBusy]="{busy: busy,message: 'Please wait...'}">
 <div *ngIf="settingView">
 <my-actions [links]="settingView.actionUrls"></my-actions>
 </div>
 </div>
  • 15.
    Angular <h4>Actions</h4>
 <div class="section">
 <a href="{{links[0]}}">ChangeYour Password</a>
 <a href="{{links[1]}}">Change Your Security Questions</a>
 <a href="{{links[2]}}">Change other usernames for a Single Sign- On Experience</a>
 </div>
  • 16.
    Angular import { Component,Input, OnInit } from '@angular/core';
 
 @Component({
 moduleId: module.id.toString(),
 selector: 'my-actions',
 templateUrl: 'actions.component.html'
 })
 export class ActionsComponent implements OnInit {
 constructor() { }
 
 @Input()
 links: string[] = [];
 
 ngOnInit() { }
 }
  • 17.
  • 18.
    React import React from'react';
 import { render } from 'react-dom';
 import Text from './form-sections/text';
 export default class Form extends React.Component {
 constructor(props) {
 super(props);
 }
 render() {
 return ( 
 <ay-form ref="form">
 <Text />
 </ay-form> 
 );
 }
 }

  • 19.
    React import React from'react';
 import { render } from 'react-dom';
 import pure from 'react-addons-pure-render-mixin';
 
 export default class Text extends React.Component {
 constructor(props) {
 super(props);
 this.shouldComponentUpdate = pure.shouldComponentUpdate.bind(this);
 }
 render() {
 return (
 <section>
 <ay-field type="text" value="" name="where" placeholder="Where"></ay-field>
 </section>
 );
 }
 }
  • 20.
  • 21.
  • 22.
    JS Frameworks +Web Components • Web Components are “just HTML” so they will work with all of them • It may require some manual wiring, like using addEventListener • With plugins, you can make use of framework template functionality • https://github.com/webcomponents/react-integration • https://github.com/clubajax/react-inject-change-events
  • 23.
  • 24.
    Web Components Benefits •Part of the DOM - lifecycle tools for free! • Future-proof and cross-browser (web standard) for creating and extending reusable components. • Requires no library or framework to get started. Vanilla JS/HTML FTW! • Provides a familiar programming model. It's just DOM/CSS/HTML. • Creates a very modular system • Tightly integrated with the browser's DevTools. • Leverage existing accessibility features.
  • 25.
    Web Components • CustomElements • Shadow DOM • Templates • HTML Imports All four of these items are a WC3 spec, all of which makes up Web Components
  • 26.
    Custom Elements • Defineyour own element types and functionality • Provides a standard way to associate JavaScript logic with an element • Lifecycle methods • No confusing context — “this” is the element • Easy to inspect in debuggers We could do custom elements with IE6… the difference is the life cycle methods <x-tabs>
 <ul>Tab 1</ul>
 <ul>Tab 2</ul>
 <ul>Tab 3</ul>
 </x-tabs>
  • 27.
    Shadow DOM Content canbe “projected” into provided slots • Shadow DOM refers to the ability of the browser to include a subtree of DOM elements into the rendering of a document, but not into the main document DOM tree. • CSS style rules are constrained to the shadow tree they were defined in. • The DOM is also encapsulated. For example, document.querySelectorAll('div'); will not return any results from a custom element with Shadow DOM
  • 28.
    Templates • The templateelement is used to declare fragments of HTML that can be cloned and inserted in the document by script. • Contains a DocumentFragment in its HTMLTemplateElement.content property
  • 29.
    HTML Imports <link rel=“import”href=“./my-bugger.html” />
 <link rel=“import” href=“./my-zapper.html” /> • Can import all in one file: JavaScript, HTML, and CSS • Effectively, an HTML document. • HTML Imports can be the dependency management system, replacing AMD or CommonJS • HTML Imports let you include and reuse HTML documents in other HTML documents, just as script tags let you include external JavaScript in their pages.
  • 30.
    HTML Imports -CONS The HTML Import spec does not have consensus. Mozilla is still holding out, after two years. <link rel=“import” href=“./my-bugger.html” />
 <link rel=“import” href=“./my-zapper.html” /> • Confusing context when importing templates • Spec essentially competes with ES modules • Globals
  • 31.
  • 32.
    Browser Support • Chromev54 • WebKit Nightly v18 • Edge has begun prototyping • Firefox has an open bug
  • 33.
  • 34.
    We need tosupport old IE because we… • want to expose our servers to unsupported, insecure browsers. • like to pay the extra IE development cost (8:25%, 9:10%, 10:5%). • enjoy tying up QA to spend extra time testing more browser versions https://www.xfive.co/blog/stop-supporting-ie10-ie9-ie8/ Seriously, management often encourages the support of old versions because they are not aware of the cost and risk.
  • 35.
    Polyfills • A polyfillfor HTML Custom Elements. • ShadyDOM provides a shim for ShadowDOM V1. It is less correct but less intrusive and faster than the ShadowDOM Polyfill. • ShadyCSS provides a shim for CSS Custom Properties, CSS Mixins with @apply support, and ShadowDOM V1 style encapsulation with the ShadyDOM library.
  • 36.
    v0 vs v1* *Thedifferences between the old spec and the recent changes
  • 37.
    v0 var proto =Object.create(HTMLElement.prototype);
 proto.createdCallback = function() {
 // invoked on creation
 };
 proto.attachedCallback = function() {
 // invoked when added to the document
 };
 proto.detachedCallback = function() {
 // invoked when removed from the document
 };
 proto.attributeChangedCallback = function(attrName, oldVal, newVal) {
 // invoked when an attribute is changed
 }; 
 var MyComponent = document.registerElement('my-component', {
 prototype: proto
 });
  • 38.
    class MyComponent extendsHTMLElement {
 static get observedAttributes() { return ['value', 'disabled']; }
 constructor ( ) {
 super();
 }
 connectedCallback ( ) { }
 disconnectedCallback ( ) { }
 attributeChangedCallback(attrName, oldVal, newVal) { }
 adoptedCallback() { }
 }
 customElements.define('my-component', MyComponent); v1 constructor: the element is created or upgraded connectedCallback: the element is inserted into the DOM disconnectedCallback: the element is removed from the DOM. attributeChangedCallback: an attribute was added, removed, or updated adoptedCallback the element has been moved into a new document define: exposes the element name for use in markup
  • 39.
    History Q: Why didthe spec change? A: The old spec did not work with ES6 classes Q: Does the new spec work wth ES5? A: uh, sort of… Q: Who changed it? A: Blame Apple's Ryosuke Niwa. He’s made a few changes… Q: Should I wait to use Web Components until v1 is implemented? A: No, not any more than you would wait for any other shim-able browser feature
  • 40.
    Inheritance class MyButton extendsHTMLButtonElement {
 constructor () {
 super(...arguments);
 }
 }
 customElements.define('my-button', MyButton, {extends: 'button'}); Extends something other than HTMLElement Extra argument when defining Blocked by Apple. Use native tag, and extended tag in “is” This is what it would look like… if it were implemented.
  • 41.
    ES6 Classes class MyComponentextends HTMLElement {
 static something () { return 'something'; }
 constructor () {
 super();
 }
 connectedCallback () {
 //
 }
 } always call super() first in the constructor. “this” is not established until after Commas are forbidden to emphasize that class definitions are different from object literals. Classes are sugar for prototypal inheritance. MyComponent is a function that inherits from the HTMLElement prototype. static is similar to: MyComponent.something = function () { return ’something’; } data properties are not allowed like in objects: {a: true} (yet) getters and setters must be used. super is used as a function in the constructor, as property references: super.someMethod();
  • 42.
    Custom Element APIConsiderations • Sync important properties with attributes • getters/setters over methods • Broadcast changes via events • Use standard properties and event names (“value”, “change”) • innerHTML — what happens, before and after initialization? • Appending children • Use :defined or alternative • Consider Shadow DOM styling pros and cons
  • 43.
  • 44.
  • 45.