Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Billions served, lessons learned: a Polymer story

Billions served, lessons learned: a Polymer story

Polymer went from an experiment to a Big Thing serving tons of users in major Google products and some of the biggest companies in the world in no time. Success is great! But there's always room for improvement. Mistakes were made. Lessons were learned. Regrets were had. Two years, hundreds of elements, and thousands of apps later, we've got a pretty good idea what makes an element and an app great, and what's changing in Polymer 2.0 as a result. Come hear what this means for the Polymer Elements, your elements and your apps, in a new episode of the Meownica show!

Monica Dinculescu

May 18, 2017
Tweet

More Decks by Monica Dinculescu

Other Decks in Programming

Transcript

  1. +Monica Dinculescu
    @notwaldorf
    BILLIONS SERVED
    LESSONS LEARNED
    (A POLYMER STORY)

    View full-size slide

  2. THE WEB IS LIKE A
    SHARK. IT HAS TO
    CONSTANTLY MOVE
    FORWARD OR IT DIES
    WOODY ALLEN

    View full-size slide

  3. THE WEB IS LIKE A
    SHARK. IT HAS TO
    CONSTANTLY MOVE
    FORWARD OR IT DIES
    POLYMER
    WOODY ALLEN

    View full-size slide

  4. POLYMER 1.0
    one size fits all

    View full-size slide




  5. ...
    <br/>Polymer({<br/>is: 'paper-input',<br/>behaviors: [...],<br/>properties: {...},<br/>listeners: {...},<br/>hostAttributes: {...},<br/>});<br/>


    View full-size slide

  6. WEB STANDARDS (v0)
    kind of a mess, tbh

    View full-size slide

  7. BASIC USE CASE
    well lit path!

    View full-size slide

  8. ADVANCED USE CASE
    ???

    View full-size slide

  9. DIFFERENT NEEDS
    leaf vs view nodes

    View full-size slide

  10. POLYMER 1.0
    one size fits all

    View full-size slide

  11. POLYMER 1.0
    POLYMER 2.0
    one size fits all
    a la carte

    View full-size slide

  12. WEB STANDARDS (v1)
    are a go!

    View full-size slide

  13. YOU USE

    THE PLATFORM
    WE EMPOWER YOU

    View full-size slide

  14. POLYMER 1.0
    “do less to be fast”

    View full-size slide

  15. POLYMER 1.0
    POLYMER 2.0
    “do less to be fast”
    “do more if you need to”

    View full-size slide

  16. WE HAVE OPTIONS!

    View full-size slide

  17. HTMLElement
    getters/setters

    View full-size slide

  18. HTMLElement
    getters/setters
    templates

    View full-size slide

  19. HTMLElement
    getters/setters
    templates
    {{ }}

    View full-size slide

  20. HTMLElement
    getters/setters
    templates
    {{ }}

    View full-size slide

  21. HTMLElement
    PropertyAccessors
    TemplateStamp
    PropertyEffects
    Polymer.Element

    View full-size slide

  22. HTMLElement
    PropertyAccessors
    TemplateStamp
    PropertyEffects
    Polymer.Element
    2.2 KB
    3.4 KB
    8.8 KB
    11 KB

    View full-size slide



  23. myElement.counter = 3;

    View full-size slide

  24. aspiring-chauffeur.glitch.me

    View full-size slide

  25. HTMLElement
    Polymer.PropertyAccessors
    Polymer.TemplateStamp
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  26. class MyElement extends HTMLElement {
    ...
    }
    customElements.define('my-element', MyElement);

    View full-size slide

  27. Lifecycle methods!
    class MyElement extends HTMLElement {
    constructor() { ... }
    connectedCallback() { ... }
    disconnectedCallback() { ... }
    attributeChangedCallback(attr, oldValue, newValue) { ... }
    static get observedAttributes() { return [...]; }
    }

    View full-size slide

  28. Lifecycle methods!
    class MyElement extends HTMLElement {
    constructor() { ... }
    connectedCallback() { ... }
    disconnectedCallback() { ... }
    attributeChangedCallback(attr, oldValue, newValue) { ... }
    static get observedAttributes() { return [...]; }
    }

    View full-size slide

  29. Lifecycle methods!
    class MyElement extends HTMLElement {
    constructor() { ... }
    connectedCallback() { ... }
    disconnectedCallback() { ... }
    attributeChangedCallback(attr, oldValue, newValue) { ... }
    static get observedAttributes() { return [...]; }
    }

    View full-size slide

  30. Lifecycle methods!
    class MyElement extends HTMLElement {
    constructor() { ... }
    connectedCallback() { ... }
    disconnectedCallback() { ... }
    attributeChangedCallback(attr, oldValue, newValue) { ... }
    static get observedAttributes() { return [...]; }
    }

    View full-size slide

  31. class MyElement extends HTMLElement {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() { this.render(); }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }

    View full-size slide

  32. class MyElement extends HTMLElement {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() { this.render(); }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }
    Make the shadow root

    View full-size slide

  33. class MyElement extends HTMLElement {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() { this.render(); }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }
    Add things to it

    View full-size slide

  34. render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment.bind(this));
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    // Some styles for pretty.
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }

    View full-size slide

  35. render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment.bind(this));
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    // Some styles for pretty.
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }

    View full-size slide

  36. render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment.bind(this));
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    // Some styles for pretty.
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }

    View full-size slide

  37. class MyElement extends HTMLElement {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() { this.render(); }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }
    Update properties

    View full-size slide

  38. class MyElement extends HTMLElement {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() { this.render(); }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }
    Optional: reflect attribute

    View full-size slide

  39. class MyElement extends HTMLElement {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() { this.render(); }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }
    Actual useful code

    View full-size slide

  40. class MyElement extends HTMLElement {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() { this.render(); }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }
    display() {
    this.output.innerHTML =
    ''.repeat(this.counter);
    }
    Actual useful code

    View full-size slide

  41. ONCE MORE,
    WITH FEELING LESS
    BOILERPLATE

    View full-size slide

  42. HTMLElement
    Polymer.PropertyAccessors
    Polymer.TemplateStamp
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  43. class MyElement extends HTMLElement {
    constructor() { ... }
    connectedCallback() {
    this.render();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }

    View full-size slide

  44. class MyElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() { ... }
    connectedCallback() {
    this.render();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }

    View full-size slide

  45. class MyElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() { ... }
    connectedCallback() {
    this.render();
    this._enableProperties();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    }
    Turn on accessors

    View full-size slide

  46. class MyElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() { ... }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }
    Ready!

    View full-size slide

  47. class MyElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() { ... }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }

    View full-size slide

  48. class MyElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() { ... }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    attributeChangedCallback(attr, oldValue, newValue) {
    if (oldValue !== newValue) {
    this[attr] = newValue;
    }
    }
    get counter() { return this._counter; }
    set counter(value) {
    if (value != this._counter) {
    this._counter = parseInt(value);
    this.setAttribute('counter', value);
    this.display();
    }

    View full-size slide

  49. class MyElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() { ... }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) {
    if ('counter' in changedProps) { // Only reflect this
    this.setAttribute('counter', changedProps.counter);
    this.display();
    }
    }
    }
    Actual useful code!

    View full-size slide

  50. class MyElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() { ... }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) {
    if ('counter' in changedProps) // Only reflect this
    this.setAttribute(‘counter’, changedProps.counter);
    this.display();
    }
    }
    }
    MyHTMLElement.createPropertiesForAttributes(); Do the boilerplate!

    View full-size slide

  51. HTMLElement
    Polymer.PropertyAccessors
    Polymer.TemplateStamp
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  52. CSS/HTML in JS
    render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment);
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }

    View full-size slide

  53. render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment);
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }
    CSS/HTML in JS

    View full-size slide

  54. CSS/HTML in CSS/HTML
    render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment);
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }

    <br/>:host {<br/>display: block;<br/>font-size: 30px;<br/>}<br/>span { margin-left: 10px; }<br/>button {<br/>background: transparent;<br/>font-size: inherit;<br/>border: none;<br/>cursor: pointer;<br/>}<br/>



    View full-size slide

  55. CSS/HTML in CSS/HTML

    <br/>:host {<br/>display: block;<br/>font-size: 30px;<br/>}<br/>span { margin-left: 10px; }<br/>button {<br/>background: transparent;<br/>font-size: inherit;<br/>border: none;<br/>cursor: pointer;<br/>}<br/>



    render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment);
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }

    View full-size slide

  56. render() {
    var button = document.createElement('button');
    button.innerHTML = '';
    button.addEventListener('click', this.increment);
    this.shadowRoot.appendChild(button);
    this.output = document.createElement('span');
    this.shadowRoot.appendChild(this.output);
    this.style.display = 'block';
    this.style.fontSize = '30px';
    this.output.style.marginLeft = '10px';
    button.style.background = 'transparent';
    button.style.fontSize = 'inherit';
    button.style.border = 'none';
    button.style.cursor = 'pointer';
    }

    <br/>:host {<br/>display: block;<br/>font-size: 30px;<br/>}<br/>span { margin-left: 10px; }<br/>button {<br/>background: transparent;<br/>font-size: inherit;<br/>border: none;<br/>cursor: pointer;<br/>}<br/>



    CSS/HTML in CSS/HTML

    View full-size slide


  57. <br/>:host {<br/>display: block;<br/>font-size: 30px;<br/>}<br/>span { margin-left: 10px; }<br/>button {<br/>background: transparent;<br/>font-size: inherit;<br/>border: none;<br/>cursor: pointer;<br/>}<br/>



    Declarative
    CSS/HTML in CSS/HTML

    View full-size slide


  58. <br/>:host {<br/>display: block;<br/>font-size: 30px;<br/>}<br/>span { margin-left: 10px; }<br/>button {<br/>background: transparent;<br/>font-size: inherit;<br/>border: none;<br/>cursor: pointer;<br/>}<br/>



    this.$.output
    CSS/HTML in CSS/HTML

    View full-size slide

  59. WHICH MEANS EVEN
    LESS BOILERPLATE!

    View full-size slide

  60. class MyHTMLElement extends Polymer.PropertyAccessors(HTMLElement) {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) { ... }
    display() { ... }
    increment() { ... }
    render() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();

    View full-size slide

  61. class MyHTMLElement extends Polymer.TemplateStamp(Polymer.PropertyAccessors(HTMLElement)) {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) { ... }
    display() { ... }
    increment() { ... }
    render() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();

    View full-size slide

  62. class MyHTMLElement extends Polymer.TemplateStamp(Polymer.PropertyAccessors(HTMLElement)) {
    constructor() {
    super();
    this._counter = 0;
    this.attachShadow({mode: 'open'});
    }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.render();
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) { ... }
    display() { ... }
    increment() { ... }
    render() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();
    No more manual render!

    View full-size slide

  63. class MyHTMLElement extends Polymer.TemplateStamp(Polymer.PropertyAccessors(HTMLElement)) {
    constructor() {
    super();
    this._counter = 0;
    }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) { ... }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();

    View full-size slide

  64. class MyHTMLElement extends Polymer.TemplateStamp(Polymer.PropertyAccessors(HTMLElement)) {
    constructor() {
    super();
    this._counter = 0;
    }
    connectedCallback() {
    this._enableProperties();
    }
    ready() {
    this.dom = this._stampTemplate(someTemplate);
    this.attachShadow({mode: 'open'}).appendChild(this.dom);
    super.ready();
    }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) { ... }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();
    Stamp the template

    View full-size slide

  65. HTMLElement
    Polymer.PropertyAccessors
    Polymer.TemplateStamp
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  66. class MyHTMLElement extends
    Polymer.TemplateStamp(Polymer.PropertyAccessors(HTMLElement)) {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) {
    if ('counter' in changedProps)
    this.setAttribute(‘counter’, changedProps.counter);
    this.display();
    }
    }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();

    View full-size slide

  67. class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) {
    if ('counter' in changedProps)
    this.setAttribute(‘counter’, changedProps.counter);
    this.display();
    }
    }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();

    View full-size slide

  68. class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    _propertiesChanged(currentProps, changedProps, oldProps) {
    if ('counter' in changedProps)
    this.setAttribute(‘counter’, changedProps.counter);
    this.display();
    }
    }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();

    View full-size slide

  69. class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();

    View full-size slide

  70. class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();
    MyHTMLElement.createReflectedProperty('counter'); Do the boilerplate!

    View full-size slide

  71. Data binding!

    /* ... */



    display() {
    this.$.output.innerHTML = ''.repeat(this.counter);
    }

    View full-size slide

  72. Data binding!

    /* ... */

    [[display(counter)]]

    display(c) {
    return ''.repeat(c);
    }

    View full-size slide

  73. HTMLElement
    Polymer.PropertyAccessors
    Polymer.TemplateStamp
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  74. class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();
    MyHTMLElement.createReflectedProperty('counter');

    View full-size slide

  75. class MyHTMLElement extends Polymer.Element {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();
    MyHTMLElement.createReflectedProperty('counter');

    View full-size slide

  76. class MyHTMLElement extends Polymer.Element {
    constructor() { ... }
    connectedCallback() { ... }
    ready() { ... }
    static get observedAttributes() { return ['counter', 'limit']; }
    display() { ... }
    increment() { ... }
    }
    MyHTMLElement.createPropertiesForAttributes();
    MyHTMLElement.createReflectedProperty('counter');

    View full-size slide

  77. class MyHTMLElement extends Polymer.Element {
    static get properties() {
    return {
    counter: {type: Number, reflectToAttribute: true, value: 0},
    limit: {type: Number}
    }
    }
    display() { ... }
    increment() { ... }
    }

    View full-size slide

  78. class MyHTMLElement extends Polymer.Element {
    static get is() { return 'my-element'; }
    static get properties() {
    return {
    counter: {type: Number, reflectToAttribute: true, value: 0},
    limit: {type: Number}
    }
    }
    display() { ... }
    increment() { ... }
    }
    Automatic template finding

    View full-size slide

  79. Automatic template finding


    ...

    <br/>class MyHTMLElement extends Polymer.Element {<br/>...<br/>}<br/>customElements.define(MyHTMLElement.is, MyHTMLElement);<br/>

    View full-size slide

  80. Automatic template finding


    ...

    <br/>class MyHTMLElement extends Polymer.Element {<br/>...<br/>}<br/>customElements.define(MyHTMLElement.is, MyHTMLElement);<br/>


    View full-size slide

  81. HTMLElement
    Polymer.PropertyAccessors
    Polymer.TemplateStamp
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  82. HTMLElement
    Getters and setters
    Polymer.TemplateStamp
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  83. HTMLElement
    Getters and setters
    CSS in JS -> templates
    Polymer.PropertyEffects
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  84. HTMLElement
    Getters and setters
    CSS in JS -> templates
    Data binding
    Polymer.Element
    1
    2
    3
    4
    5

    View full-size slide

  85. HTMLElement
    Getters and setters
    CSS in JS -> templates
    Data binding
    Everything + all the convenience
    1
    2
    3
    4
    5

    View full-size slide

  86. POLYMER 2.0
    kinda like jQuery for Web
    Components

    View full-size slide

  87. POLYMER 2.0
    kinda like jQuery for Web
    Components. In the sense
    that it helps you out not
    that it’s old and boring.

    View full-size slide

  88. ES6 CLASSES:
    LESS LIBRARY
    MORE PLATFORM

    View full-size slide

  89. class MyHTMLElement extends Polymer.Element {
    static get is() { return 'my-element'; }
    static get properties() {
    return {
    counter: {type: Number, reflectToAttribute: true, value: 0},
    limit: {type: Number}
    }
    }
    display() { ... }
    increment() { ... }
    }

    View full-size slide

  90. class MyHTMLElement extends Polymer.Element {
    static get is() { return 'my-element'; }
    static get properties() {
    return {
    counter: {type: Number, reflectToAttribute: true, value: 0},
    limit: {type: Number}
    }
    }
    display() { ... }
    increment() { ... }
    }
    Inheritance

    View full-size slide

  91. AND SPEAKING OF
    INHERITANCE…

    View full-size slide

  92. Base class


    ...

    <br/>class MyHTMLElement extends Polymer.Element {<br/>static get is() { return 'my-element'; }<br/>display() {...}<br/>...<br/>}<br/>customElements.define(MyHTMLElement.is, MyHTMLElement);<br/>

    View full-size slide


  93. <br/>class MyOtherElement extends MyHTMLElement {<br/>static get is() { return 'other-element'; }<br/>display(c) {<br/>return ''.repeat(c);<br/>}<br/>}<br/>customElements.define(MyOtherElement.is, MyOtherElement);<br/>

    Child class
    Base Class

    View full-size slide


  94. <br/>class MyOtherElement extends MyHTMLElement {<br/>static get is() { return 'other-element'; }<br/>display(c) {<br/>return ''.repeat(c);<br/>}<br/>}<br/>customElements.define(MyOtherElement.is, MyOtherElement);<br/>

    Child class
    Override base class methods

    View full-size slide



  95. ([[counter]])

    <br/>class MyOtherElement extends MyHTMLElement {<br/>static get is() { return 'other-element'; }<br/>static get template() {<br/>var t = Polymer.DomModule.import(this.is, 'template');<br/>let superT = MyHTMLElement.cloneNode(true);<br/>}<br/>customElements.define(MyOtherElement.is, MyOtherElement);<br/>

    Child class
    Override base class template

    View full-size slide



  96. ([[counter]])

    <br/>class MyOtherElement extends MyHTMLElement {<br/>static get is() { return 'other-element'; }<br/>static get template() {<br/>let t = Polymer.DomModule.import(MyOtherElement.is, 'template');<br/>let superT = MyHTMLElement.template.cloneNode(true);<br/>return t.content.insertBefore(t.content.firstChild, superT);<br/>}<br/>}<br/>customElements.define(MyOtherElement.is, MyOtherElement);<br/>
    Child class
    Override base class template

    View full-size slide

  97. POLYMER 2.0
    HELPS ELEMENTS.

    View full-size slide


  98. USE NO SUGAR.

    View full-size slide


  99. USE INHERITANCE.

    View full-size slide




  100. route="[[subroute]]"
    category-name="{{categoryName}}"
    category="[[category]]"
    loading="[[loading]]"
    offline="[[offline]]"
    failure="[[failure]]">

    route="{{subroute}}"
    category-name="{{categoryName}}"
    category="[[category]]"
    article="[[article]]"
    loading="[[loading]]"
    offline="[[offline]]"
    failure="[[failure]]">


    View full-size slide




  101. route="[[subroute]]"
    category-name="{{categoryName}}"
    category="[[category]]"
    loading="[[loading]]"
    offline="[[offline]]"
    failure="[[failure]]">

    route="{{subroute}}"
    category-name="{{categoryName}}"
    category="[[category]]"
    article="[[article]]"
    loading="[[loading]]"
    offline="[[offline]]"
    failure="[[failure]]">


    View full-size slide


  102. USE EVERYTHING.

    View full-size slide

  103. POLYMER 2.0
    HELPS APPS.

    View full-size slide

  104. SHADOW STYLING
    FEELS TOO STRICT?

    View full-size slide

  105. Just override!
    class MyHTMLElement extends Polymer.Element {
    static get is() { return 'my-element'; }
    attachDom(dom) {
    return this.attachShadow({mode:'open'}).appendChild(dom);
    }
    }

    View full-size slide

  106. Just override!
    class MyHTMLElement extends Polymer.Element {
    static get is() { return 'my-element'; }
    attachDom(dom) {
    return this.appendChild(dom);
    }
    }

    View full-size slide

  107. DEFINE ELEMENTS

    IN SCRIPT NOT 

    IMPORTS?

    View full-size slide

  108. Just override!
    class MyHTMLElement extends Polymer.Element {
    static get is() { return 'my-element'; }
    static get template() {
    return `
    <br/>…<br/>

    [[display(counter)]]
    `
    }
    }

    View full-size slide

  109. WHEN IN DOUBT
    JUST OVERRIDE.

    View full-size slide

  110. WHEN YOU COME
    TO A FORK IN THE
    ROAD, TAKE IT.
    YOGI BERRA

    View full-size slide

  111. Come say hi!
    +Monica Dinculescu
    @notwaldorf
    Icons: Gan Khoon Lay from Noun Project

    View full-size slide