Share
Embed
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
Transcript
122 slides
-
THE WEB IS LIKE A SHARK. IT HAS TO CONSTANTLY
MOVE FORWARD OR IT DIES WOODY ALLEN -
THE WEB IS LIKE A SHARK. IT HAS TO CONSTANTLY
MOVE FORWARD OR IT DIES POLYMER WOODY ALLEN -
<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> -
POLYMER 1.0 POLYMER 2.0 “do less to be fast” “do
more if you need to” -
Lifecycle methods! class MyElement extends HTMLElement { constructor() { ...
} connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attr, oldValue, newValue) { ... } static get observedAttributes() { return [...]; } } -
Lifecycle methods! class MyElement extends HTMLElement { constructor() { ...
} connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attr, oldValue, newValue) { ... } static get observedAttributes() { return [...]; } } -
Lifecycle methods! class MyElement extends HTMLElement { constructor() { ...
} connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attr, oldValue, newValue) { ... } static get observedAttributes() { return [...]; } } -
Lifecycle methods! class MyElement extends HTMLElement { constructor() { ...
} connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attr, oldValue, newValue) { ... } static get observedAttributes() { return [...]; } } -
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(); } } -
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 -
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 -
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'; } -
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'; } -
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'; } -
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 -
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 -
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 -
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 -
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(); } } -
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(); } } -
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 -
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! -
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(); } -
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(); } -
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! -
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! -
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'; } -
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 -
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> -
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'; } -
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 -
<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 -
<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 -
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(); -
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(); -
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! -
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(); -
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 -
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(); -
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(); -
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(); -
class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) { constructor() { ... } connectedCallback()
{ ... } ready() { ... } static get observedAttributes() { return ['counter', 'limit']; } display() { ... } increment() { ... } } MyHTMLElement.createPropertiesForAttributes(); -
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! -
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); } -
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); } -
class MyHTMLElement extends Polymer.PropertyEffects(HTMLElement)) { constructor() { ... } connectedCallback()
{ ... } ready() { ... } static get observedAttributes() { return ['counter', 'limit']; } display() { ... } increment() { ... } } MyHTMLElement.createPropertiesForAttributes(); MyHTMLElement.createReflectedProperty('counter'); -
class MyHTMLElement extends Polymer.Element { constructor() { ... } connectedCallback()
{ ... } ready() { ... } static get observedAttributes() { return ['counter', 'limit']; } display() { ... } increment() { ... } } MyHTMLElement.createPropertiesForAttributes(); MyHTMLElement.createReflectedProperty('counter'); -
class MyHTMLElement extends Polymer.Element { constructor() { ... } connectedCallback()
{ ... } ready() { ... } static get observedAttributes() { return ['counter', 'limit']; } display() { ... } increment() { ... } } MyHTMLElement.createPropertiesForAttributes(); MyHTMLElement.createReflectedProperty('counter'); -
class MyHTMLElement extends Polymer.Element { static get properties() { return
{ counter: {type: Number, reflectToAttribute: true, value: 0}, limit: {type: Number} } } display() { ... } increment() { ... } } -
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 -
Automatic template finding <dom-module id="my-element"> <template> ... </template> <script> class
MyHTMLElement extends Polymer.Element { ... } customElements.define(MyHTMLElement.is, MyHTMLElement); </script> </dom-module> -
Automatic template finding <dom-module id="my-element"> <template> ... </template> <script> class
MyHTMLElement extends Polymer.Element { ... } customElements.define(MyHTMLElement.is, MyHTMLElement); </script> </dom-module> -
HTMLElement Getters and setters CSS in JS -> templates Polymer.PropertyEffects
Polymer.Element 1 2 3 4 5 -
HTMLElement Getters and setters CSS in JS -> templates Data
binding Polymer.Element 1 2 3 4 5 -
HTMLElement Getters and setters CSS in JS -> templates Data
binding Everything + all the convenience 1 2 3 4 5 -
POLYMER 2.0 kinda like jQuery for Web Components. In the
sense that it helps you out not that it’s old and boring. -
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() { ... } } -
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 -
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> -
<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 -
<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 -
<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 -
<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 -
<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> -
<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> -
Just override! class MyHTMLElement extends Polymer.Element { static get is()
{ return 'my-element'; } attachDom(dom) { return this.attachShadow({mode:'open'}).appendChild(dom); } } -
Just override! class MyHTMLElement extends Polymer.Element { static get is()
{ return 'my-element'; } attachDom(dom) { return this.appendChild(dom); } } -
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> ` } } -
WHEN YOU COME TO A FORK IN THE ROAD, TAKE
IT. YOGI BERRA -
Come say hi! +Monica Dinculescu @notwaldorf Icons: Gan Khoon Lay
from Noun Project