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 Slide

  2. @notwaldorf

    View Slide

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

    View Slide

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

    View Slide

  5. POLYMER 1.0
    one size fits all

    View Slide




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


    View Slide

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

    View Slide

  8. BASIC USE CASE
    well lit path!

    View Slide

  9. ADVANCED USE CASE
    ???

    View Slide

  10. DIFFERENT NEEDS
    leaf vs view nodes

    View Slide

  11. POLYMER 1.0
    one size fits all

    View Slide

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

    View Slide

  13. WEB STANDARDS (v1)
    are a go!

    View Slide

  14. YOU USE

    THE PLATFORM
    WE EMPOWER YOU

    View Slide

  15. POLYMER 1.0
    “do less to be fast”

    View Slide

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

    View Slide

  17. WE HAVE OPTIONS!

    View Slide

  18. HTMLElement

    View Slide

  19. HTMLElement
    getters/setters

    View Slide

  20. HTMLElement
    getters/setters
    templates

    View Slide

  21. HTMLElement
    getters/setters
    templates
    {{ }}

    View Slide

  22. HTMLElement
    getters/setters
    templates
    {{ }}

    View Slide

  23. HTMLElement
    PropertyAccessors
    TemplateStamp
    PropertyEffects
    Polymer.Element

    View Slide

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

    View Slide



  25. myElement.counter = 3;

    View Slide

  26. aspiring-chauffeur.glitch.me

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View 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();
    }
    }

    View Slide

  34. 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 Slide

  35. 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 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 Slide

  37. 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 Slide

  38. 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 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();
    }
    }
    Update properties

    View 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();
    }
    }
    Optional: reflect attribute

    View Slide

  41. 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 Slide

  42. 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 Slide

  43. ONCE MORE,
    WITH FEELING LESS
    BOILERPLATE

    View Slide

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

    View Slide

  45. 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 Slide

  46. 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 Slide

  47. 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 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();
    }
    Ready!

    View Slide

  49. 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 Slide

  50. 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 Slide

  51. 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 Slide

  52. 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 Slide

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

    View Slide

  54. 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 Slide

  55. 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 Slide

  56. 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 Slide

  57. 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 Slide

  58. 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 Slide


  59. <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 Slide


  60. <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 Slide

  61. WHICH MEANS EVEN
    LESS BOILERPLATE!

    View Slide

  62. 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 Slide

  63. 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 Slide

  64. 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 Slide

  65. 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 Slide

  66. 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 Slide

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

    View Slide

  68. 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 Slide

  69. 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 Slide

  70. 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 Slide

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

    View Slide

  72. 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 Slide

  73. Data binding!

    /* ... */



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

    View Slide

  74. Data binding!

    /* ... */

    [[display(counter)]]

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

    View Slide

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

    View Slide

  76. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  81. 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 Slide

  82. Automatic template finding


    ...

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

    View Slide

  83. Automatic template finding


    ...

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


    View Slide

  84. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  90. POLYMER 2.0
    kinda like jQuery for Web
    Components

    View Slide

  91. 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 Slide

  92. LIKE…

    View Slide

  93. ES6 CLASSES:
    LESS LIBRARY
    MORE PLATFORM

    View Slide

  94. 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 Slide

  95. 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 Slide

  96. AND SPEAKING OF
    INHERITANCE…

    View Slide

  97. 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 Slide


  98. <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 Slide


  99. <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 Slide



  100. ([[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 Slide



  101. ([[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 Slide

  102. POLYMER 2.0
    HELPS ELEMENTS.

    View Slide


  103. View Slide


  104. View Slide


  105. USE NO SUGAR.

    View Slide


  106. View Slide


  107. View Slide


  108. View Slide


  109. USE INHERITANCE.

    View Slide


  110. View Slide




  111. 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 Slide




  112. 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 Slide


  113. USE EVERYTHING.

    View Slide

  114. POLYMER 2.0
    HELPS APPS.

    View Slide

  115. SHADOW STYLING
    FEELS TOO STRICT?

    View Slide

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

    View Slide

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

    View Slide

  118. DEFINE ELEMENTS

    IN SCRIPT NOT 

    IMPORTS?

    View Slide

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

    [[display(counter)]]
    `
    }
    }

    View Slide

  120. WHEN IN DOUBT
    JUST OVERRIDE.

    View Slide

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

    View Slide

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

    View Slide