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!

053e75a5b48b44d6dd0612795dfb326d?s=128

Monica Dinculescu

May 18, 2017
Tweet

Transcript

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

  2. @notwaldorf

  3. THE WEB IS LIKE A SHARK. IT HAS TO CONSTANTLY

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

    MOVE FORWARD OR IT DIES POLYMER WOODY ALLEN
  5. POLYMER 1.0 one size fits all

  6. <link rel="import" href="../polymer/polymer.html"> <dom-module id="paper-input"> <template> <style>...</style> <script> Polymer({ is:

    'paper-input', behaviors: [...], properties: {...}, listeners: {...}, hostAttributes: {...}, }); </script> </template> </dom-module>
  7. WEB STANDARDS (v0) kind of a mess, tbh

  8. BASIC USE CASE well lit path!

  9. ADVANCED USE CASE ???

  10. DIFFERENT NEEDS leaf vs view nodes

  11. POLYMER 1.0 one size fits all

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

    carte
  13. WEB STANDARDS (v1) are a go!

  14. YOU USE
 THE PLATFORM WE EMPOWER YOU

  15. POLYMER 1.0 “do less to be fast”

  16. POLYMER 1.0 POLYMER 2.0 “do less to be fast” “do

    more if you need to”
  17. WE HAVE OPTIONS!

  18. HTMLElement

  19. HTMLElement getters/setters

  20. HTMLElement getters/setters templates

  21. HTMLElement getters/setters templates {{ }}

  22. HTMLElement getters/setters templates {{ }}

  23. HTMLElement PropertyAccessors TemplateStamp PropertyEffects Polymer.Element

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

    KB 11 KB
  25. <my-element limit=5> <my-element counter=3 limit=10> myElement.counter = 3;

  26. aspiring-chauffeur.glitch.me

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

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

  29. Lifecycle methods! class MyElement extends HTMLElement { constructor() { ...

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

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

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

    } connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attr, oldValue, newValue) { ... } static get observedAttributes() { return [...]; } }
  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(); } }
  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
  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
  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'; }
  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'; }
  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'; }
  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
  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
  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
  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
  43. ONCE MORE, WITH FEELING LESS BOILERPLATE

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

  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(); } }
  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(); } }
  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
  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!
  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(); }
  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(); }
  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!
  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!
  53. HTMLElement Polymer.PropertyAccessors Polymer.TemplateStamp Polymer.PropertyEffects Polymer.Element 1 2 3 4 5

  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'; }
  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
  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'; } <template id="my-template"> <style> :host { display: block; font-size: 30px; } span { margin-left: 10px; } button { background: transparent; font-size: inherit; border: none; cursor: pointer; } </style> <button on-click="increment"></button> <span id="output"></span> </template>
  57. CSS/HTML in CSS/HTML <template id="my-template"> <style> :host { display: block;

    font-size: 30px; } span { margin-left: 10px; } button { background: transparent; font-size: inherit; border: none; cursor: pointer; } </style> <button on-click="increment"></button> <span id="output"></span> </template> 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'; }
  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'; } <template id="my-template"> <style> :host { display: block; font-size: 30px; } span { margin-left: 10px; } button { background: transparent; font-size: inherit; border: none; cursor: pointer; } </style> <button on-click="increment"></button> <span id="output"></span> </template> CSS/HTML in CSS/HTML
  59. <template id="my-template"> <style> :host { display: block; font-size: 30px; }

    span { margin-left: 10px; } button { background: transparent; font-size: inherit; border: none; cursor: pointer; } </style> <button on-click="increment"></button> <span id="output"></span> </template> Declarative CSS/HTML in CSS/HTML
  60. <template id="my-template"> <style> :host { display: block; font-size: 30px; }

    span { margin-left: 10px; } button { background: transparent; font-size: inherit; border: none; cursor: pointer; } </style> <button on-click="increment"></button> <span id="output"></span> </template> this.$.output CSS/HTML in CSS/HTML
  61. WHICH MEANS EVEN LESS BOILERPLATE!

  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();
  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();
  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!
  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();
  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
  67. HTMLElement Polymer.PropertyAccessors Polymer.TemplateStamp Polymer.PropertyEffects Polymer.Element 1 2 3 4 5

  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();
  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();
  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();
  71. class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) { constructor() { ... } connectedCallback()

    { ... } ready() { ... } static get observedAttributes() { return ['counter', 'limit']; } display() { ... } increment() { ... } } MyHTMLElement.createPropertiesForAttributes();
  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!
  73. Data binding! <template id="my-template"> <style>/* ... */</style> <button on-click="increment"></button> <span

    id="output"></span> </template> display() { this.$.output.innerHTML = ''.repeat(this.counter); }
  74. Data binding! <template id="my-template"> <style>/* ... */</style> <button on-click="increment"></button> <span

    id="output">[[display(counter)]]</span> </template> display(c) { return ''.repeat(c); }
  75. HTMLElement Polymer.PropertyAccessors Polymer.TemplateStamp Polymer.PropertyEffects Polymer.Element 1 2 3 4 5

  76. None
  77. class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) { constructor() { ... } connectedCallback()

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

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

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

    { counter: {type: Number, reflectToAttribute: true, value: 0}, limit: {type: Number} } } display() { ... } increment() { ... } }
  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
  82. Automatic template finding <dom-module id="my-element"> <template> ... </template> <script> class

    MyHTMLElement extends Polymer.Element { ... } customElements.define(MyHTMLElement.is, MyHTMLElement); </script> </dom-module>
  83. Automatic template finding <dom-module id="my-element"> <template> ... </template> <script> class

    MyHTMLElement extends Polymer.Element { ... } customElements.define(MyHTMLElement.is, MyHTMLElement); </script> </dom-module>
  84. None
  85. HTMLElement Polymer.PropertyAccessors Polymer.TemplateStamp Polymer.PropertyEffects Polymer.Element 1 2 3 4 5

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

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

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

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

    binding Everything + all the convenience 1 2 3 4 5
  90. POLYMER 2.0 kinda like jQuery for Web Components

  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.
  92. LIKE…

  93. ES6 CLASSES: LESS LIBRARY MORE PLATFORM

  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() { ... } }
  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
  96. AND SPEAKING OF INHERITANCE…

  97. Base class <dom-module id="my-element"> <template> ... </template> <script> class MyHTMLElement

    extends Polymer.Element { static get is() { return 'my-element'; } display() {...} ... } customElements.define(MyHTMLElement.is, MyHTMLElement); </script> </dom-module>
  98. <dom-module id="other-element"> <script> class MyOtherElement extends MyHTMLElement { static get

    is() { return 'other-element'; } display(c) { return ''.repeat(c); } } customElements.define(MyOtherElement.is, MyOtherElement); </script> </dom-module> Child class Base Class
  99. <dom-module id="other-element"> <script> class MyOtherElement extends MyHTMLElement { static get

    is() { return 'other-element'; } display(c) { return ''.repeat(c); } } customElements.define(MyOtherElement.is, MyOtherElement); </script> </dom-module> Child class Override base class methods
  100. <dom-module id="other-element"> <template> <span id="footer">([[counter]])</span> </template> <script> class MyOtherElement extends

    MyHTMLElement { static get is() { return 'other-element'; } static get template() { var t = Polymer.DomModule.import(this.is, 'template'); let superT = MyHTMLElement.cloneNode(true); } customElements.define(MyOtherElement.is, MyOtherElement); </script> </dom-module> Child class Override base class template
  101. <dom-module id="other-element"> <template> <span id="footer">([[counter]])</span> </template> <script> class MyOtherElement extends

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

  103. <PAPER-CHECKBOX>

  104. <PAPER-CHECKBOX>

  105. <PAPER-CHECKBOX> USE NO SUGAR.

  106. <PAPER-INPUT>

  107. <GOLD-CC-INPUT>

  108. <PAPER-INPUT>

  109. <GOLD-CC-INPUT> USE INHERITANCE.

  110. <NEWS-ARTICLE>

  111. <app-route route="{{route}}" data="{{routeData}}" tail="{{subroute}}"> </app-route> <iron-pages selected="[[routeData.page]]"> <news-list id="list" name="list"

    route="[[subroute]]" category-name="{{categoryName}}" category="[[category]]" loading="[[loading]]" offline="[[offline]]" failure="[[failure]]"> </news-list> <news-article article-id="{{articleId}}" name="article" route="{{subroute}}" category-name="{{categoryName}}" category="[[category]]" article="[[article]]" loading="[[loading]]" offline="[[offline]]" failure="[[failure]]"> </news-article> </iron-pages>
  112. <app-route route="{{route}}" data="{{routeData}}" tail="{{subroute}}"> </app-route> <iron-pages selected="[[routeData.page]]"> <news-list id="list" name="list"

    route="[[subroute]]" category-name="{{categoryName}}" category="[[category]]" loading="[[loading]]" offline="[[offline]]" failure="[[failure]]"> </news-list> <news-article article-id="{{articleId}}" name="article" route="{{subroute}}" category-name="{{categoryName}}" category="[[category]]" article="[[article]]" loading="[[loading]]" offline="[[offline]]" failure="[[failure]]"> </news-article> </iron-pages>
  113. <NEWS-ARTICLE> USE EVERYTHING.

  114. POLYMER 2.0 HELPS APPS.

  115. SHADOW STYLING FEELS TOO STRICT?

  116. Just override! class MyHTMLElement extends Polymer.Element { static get is()

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

    { return 'my-element'; } attachDom(dom) { return this.appendChild(dom); } }
  118. DEFINE ELEMENTS
 IN SCRIPT NOT 
 IMPORTS?

  119. Just override! class MyHTMLElement extends Polymer.Element { static get is()

    { return 'my-element'; } static get template() { return ` <style> … </style> <button on-click="increment"></button> <span id=“output”>[[display(counter)]]</span> ` } }
  120. WHEN IN DOUBT JUST OVERRIDE.

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

    IT. YOGI BERRA
  122. Come say hi! +Monica Dinculescu @notwaldorf Icons: Gan Khoon Lay

    from Noun Project