Custom Elements Everywhere

95c3a3b33ea51545229c625bef42e343?s=47 Rob Dodson
August 25, 2017

Custom Elements Everywhere

Video: https://www.youtube.com/watch?v=sK1ODp0nDbM

If you're a mid to large size company, building your UI in Web Components and sharing it across teams on different stacks sounds like a great plan. In theory things should "just work," but reality often proves otherwise. What do we need to do to make sure our components work well with all of the major frameworks and conversely what do the frameworks need to do to work with our components? This talk explores both sides of the problem and lays out some best practices that both parties can use to ensure seamless interoperability.

95c3a3b33ea51545229c625bef42e343?s=128

Rob Dodson

August 25, 2017
Tweet

Transcript

  1. None
  2. Polymer Summit 2017 Rob Dodson @rob_dodson Custom Elements Everywhere

  3. I've been at Google for 3+ years

  4. None
  5. None
  6. Custom Elements should work everywhere.

  7. Custom Elements should work everywhere. So… why don't they?

  8. Did we put the before the unihorse

  9. TWO PARTS PART no. 1 What is a "good" Custom

    Element? PART no. 2 How should frameworks act?
  10. What is a "good" Custom Element?

  11. Do you know if there are any reference Custom Elements

    I could look at which live up to this standard? Not that I'm aware of.
  12. None
  13. SCIENCE!

  14. HowTo: Components A collection of educational custom elements that demonstrate

    best practices. Examples are meant to be read, interpreted, and ported to your own elements.
  15. super(); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(template.conte } connectedCallback() { if (!this.hasAttribute('role')) this.setAttribute('role',

    'checkbox'); if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', 0); this._upgradeProperty('checked'); this._upgradeProperty('disabled'); this.addEventListener('keydown', this._onK The construtor is a good place to create Shadow DOM, though you should avoid touching any attributes or Light DOM children as they may not be available yet. connectedCallback fires when the element is inserted into the DOM. It's a good place to set the initial role, tabindex, internal state, and install event listeners. A user may set a property on an instance of an element, before its prototype has been connected to this class. The `_upgradeProperty` method
  16. HowTo: Components are a work in progress.

  17. Check it out @ bit.ly/howto-components

  18. Shadow DOM Attributes & Properties Events

  19. Avoid absolutism

  20. Shadow DOM

  21. Does your element need Shadow DOM?

  22. You gotta use Shadow DOM! You gotta use Shadow DOM!

  23. None
  24. <howto-checkbox> howto-checkbox.js + howto-checkbox.css

  25. If someone puts your element inside another shadow root, you'll

    have to figure out how to get your styles in there.
  26. w3c/webcomponents #468

  27. Create a shadow root if you want to self-apply styles.

  28. We have CSS-in-JS for scoping styles, so why do we

    need Shadow DOM?
  29. const template = document.createElement('template'); template.innerHTML = `<div>Count: <slot></slot></div>`; Polymer Summit

    2017
  30. export class CountWithShadow extends HTMLElement { constructor () { super();

    this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); } } customElements.define('x-count-with-shadow', CountWithShadow); Polymer Summit 2017
  31. export class CountWithoutShadow extends HTMLElement { constructor () { super();

    } connectedCallback() { this.appendChild(template.content.cloneNode(true)); } } customElements.define('x-count-without-shadow', Count); Polymer Summit 2017
  32. // Meanwhile, in a React component… render () { const

    { count } = this.state; // increments every second return ( <div> <x-count-with-shadow>{count}</x-count-with-shadow> <x-count-without-shadow>{count}</x-count-without-shadow> </div> ); } Polymer Summit 2017
  33. Count: 1 1 Count:

  34. Count: 1 1 Count: <x-count-with-shadow> 1 </x-count-with-shadow>

  35. Count: 1 1 Count: <x-count-without-shadow> 1 <div>Count: <slot></slot></div> </x-count-without-shadow>

  36. Count: 1 1 Count: <x-count-without-shadow> 1 <div>Count: <slot></slot></div> </x-count-without-shadow> ummm…

  37. <x-count-without-shadow> 2 </x-count-without-shadow> Count: 2 2

  38. Place any children you create into a shadow root.

  39. These children are your implementation and should be hidden.

  40. Does your element need Shadow DOM?

  41. Yeah, it probably does.

  42. Without Shadow DOM you lose all guarantees of safety.

  43. Create a shadow root if you want to self-apply styles.

    Place any children you create into a shadow root. Shadow DOM
  44. Attributes & Properties

  45. Hey! HTML has a spec!

  46. But HTML is also kinda gnarly

  47. Accept primitive data as either attributes or properties; ideally, both.

  48. Reflect primitive data from attribute to property, and vice versa.

  49. None
  50. class CustomVideo extends HTMLElement { get preload() { const value

    = this.getAttribute('preload'); return value === null ? 'auto' : value; } set preload(value) { this.setAttribute('preload', value); } } Polymer Summit 2017
  51. class CustomVideo extends HTMLElement { get preload() { const value

    = this.getAttribute('preload'); return value === null ? 'auto' : value; } set preload(value) { this.setAttribute('preload', value); } } Polymer Summit 2017 default value
  52. class CustomVideo extends HTMLElement { get preload() { const value

    = this.getAttribute('preload'); return value === null ? 'auto' : value; } set preload(value) { this.setAttribute('preload', value); } } Polymer Summit 2017
  53. None
  54. Only accept rich data as properties.

  55. Do NOT reflect rich data properties to attributes.

  56. It's expensive to reflect.

  57. A serialized object will lose its identity.

  58. Accept primitive data as either attributes or properties. Reflect primitive

    data from attribute to property, and vice versa. Only accept rich data as properties. Do NOT reflect rich data from property to attribute. Attributes & Properties
  59. Events

  60. It's superfluous and may cause infinite loops.

  61. Do NOT dispatch events in response to the host setting

    a property (downward data flow).
  62. Do dispatch events in response to internal element activity.

  63. The element knows something changed, but the host does not.

  64. Do NOT dispatch events in response to the host setting

    a property (downward data flow). Do dispatch events in response to internal element activity. Events
  65. What is a "good" Custom Element?

  66. Resize This Window Building Components. A guide to Custom Element

    best practices.
  67. Check it out @ bit.ly/building-components

  68. How should frameworks act?

  69. Billions and billions…

  70. Custom Elements Everywhere Making sure frameworks and custom elements can

    be BFFs
  71. custom-elements-everywhere.com

  72. None
  73. Rob waves his arms around and apologizes [ ]

  74. known unknowns

  75. Custom Element test scores

  76. Attributes & Properties Events

  77. Attributes & Properties

  78. Manual The developer tells the framework how to pass data.

    Automated The framework determines how to pass data.
  79. Angular <my-element [foo]="bar">

  80. Angular <my-element [foo]="bar"> set the foo property equal to bar

  81. Angular <my-element [foo]="bar"> i.e. myElement.foo = bar

  82. Angular <my-element [foo]="bar"> <my-element [attr.baz]="squid"> set the baz attribute equal

    to squid
  83. Angular <my-element [foo]="bar"> <my-element [attr.baz]="squid"> i.e. myElement.setAttribute('baz', squid)

  84. Vue <my-element :foo="bar"> set the foo attribute equal to bar

  85. Vue <my-element :foo.prop="bar"> set the foo property equal to bar

  86. React <my-element foo={bar}> set the foo attribute equal to bar

  87. React <my-element foo={bar}> <my-element foo="[object Object]">

  88. React You can work around this by imperatively grabbing a

    reference to the element and setting the property on it.
  89. React Resize This Window facebook/react #10399

  90. Preact <my-element foo={bar}>

  91. Preact <my-element foo={bar}> set the foo property if it's available

  92. Preact if ('foo' in myElement) myElement.foo = bar else myElement.setAttribute('foo',

    bar)
  93. Preact if ('foo' in myElement) myElement.foo = bar else myElement.setAttribute('foo',

    bar)
  94. Preact if ('foo' in myElement) myElement.foo = bar else myElement.setAttribute('foo',

    bar)
  95. Preact if ('foo' in myElement) myElement.foo = bar else myElement.setAttribute('foo',

    bar)
  96. Events

  97. Angular <my-element (foochanged)="onFoo()">

  98. Angular <my-element (foochanged)="onFoo()"> myElement.addEventListener('foochanged', onFoo)

  99. Angular <my-element (foochanged)="onFoo()"> lowercase

  100. Angular <my-element (FOOChanged)="onFoo()"> CAPSCase

  101. Angular <my-element (fooChanged)="onFoo()"> camelCase

  102. Angular <my-element (foo-changed)="onFoo()"> kebab-case

  103. Angular <my-element (FooChanged)="onFoo()"> PascalCase

  104. Angular <my-element (FoOcHaNgEd)="onFoo()"> AsShOlEcAsE

  105. None
  106. Vue <my-element v-on:fooChanged="onFoo">

  107. Vue <my-element v-on:fooChanged="onFoo"> same behavior as Angular

  108. React <my-element onFooChanged={onFoo}>

  109. React <my-element onFooChanged={onFoo}> unfortunately won't work.

  110. React You can work around this by imperatively grabbing a

    reference to the element and adding an event listener.
  111. React Resize This Window facebook/react #7901

  112. Preact <my-element onFooChanged={onFoo}>

  113. Preact <my-element onFooChanged={onFoo}> TOTES WORKS!

  114. Preact <my-element onFooChanged={onFoo}> TOTES WORKS! kinda. sorta. mostly…

  115. Preact <my-element onFooChanged={onFoo}> always calls .toLowerCase() myElement.addEventListener('foochanged', onFoo)

  116. So…

  117. So… it doesn't support AsShOlEcAsE?!?!

  118. Preact Resize This Window developit/preact #788

  119. So, what did we learn?

  120. There are best practices we can all follow

  121. What's broken today seems easy to fix

  122. amazing people Reviewers HowTo: Components team Sean Rob Jason Evan

    Dan Domenic Steve Dominic Surma Monica Ewa
  123. Thank You! Polymer Summit 2017 Rob Dodson @rob_dodson Images by

    Simon Child, Eucalyp, Edward Boatman from the Noun Project.