Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Interoperable
Component Patterns
EmberConf 2016
twitter.com/mixonic
Ember.js Core Team
madhatted.com
twitter.com/mixonic
Ember.js Core Team
madhatted.com
>1,800 people!!
emberjs.com/ember-community-survey-2016
emberjs.com/ember-community-survey-2016
“The elements of this language are
entities called patterns. Each pattern
describes a problem that occurs over
and over ag...
User-centric
Designed based on
experience
A pattern is a not a guide
to implementation
~~ Prelude ~~
What we talk about
when we talk about
interoperability.
and web components
Rails server builds an HTML string
Browser parses HTML string
Component boots in the browser
Possible Consumer #1
Rails server builds an HTML string
Browser parses HTML string
Component boots in the browser
Possible Consumer #1
React template builds some DOM
Component boots in the browser
Component is implemented w/Vue.js
Possible Consumer #2
Possible Consumer #2
React template builds some DOM
Component boots in the browser
Component is implemented w/Vue.js
Riot template is rendered Server-side
Component boots on the server
Component renders and resulting
HTML goes back in the ...
Riot template is rendered Server-side
Component boots on the server
Component renders and resulting
HTML goes back in the ...
A component usable
from any client-side
consumer.
A component usable
from any client-side
consumer.
Ember Template
React/JSX
Angular 1.x Template
Angular 2 Template
DOM
HTML
~~ Pattern #1 ~~
Use Custom Elements
via a polyfill
document.createElement(

'my-component'

);
<my-component>

</my-component>
DOM
HTML
Angular 2 TemplateEmber Template
<my-component>
</my-component>
React/JSX
Angular 1.x Template
document.createElement(

'm...
1 class MyComponent extends HTMLElement {
2 attachedCallback() {
3 console.log('Now attached to the document');
4 }
5 };
6...
1 class MyComponent extends HTMLElement {
2 attachedCallback() {
3 console.log('Now attached to the document');
4 }
5 };
6...
webcomponents.org/polyfills/custom-elements
x-tag.github.io
www.polymer-project.org
Angular 2 TemplateEmber Template
React/JSX
Angular 1.x Template
DOM
HTML
<my-component>
</my-component>
document.createEle...
~~ Pattern #2 ~~
Pass attributes, then
deserialize them to
properties
~~ Pattern #2 ~~
Pass attributes, then
deserialize them to
properties on change
Component author concerns
<div id="wrapper"></div>
<div id="wrapper"></div>
let d = document.createElement('div');
d.setAttribute('id', 'wrapper');
<div id="wrapper"></div>
let d = document.createElement('div');
d.setAttribute('id', 'wrapper');
d.getAttribute('id'); // ...
let d = document.createElement('div');
d.setAttribute('wrapper-id', 'main');
<div wrapper-id="main"></div>
let d = document.createElement('div');
d.setAttribute('wrapper-id', 'main');
d.getAttribute('wrapper-id'); // => 'main'
d[...
let d = document.createElement('div');
d.setAttribute('hidden', '');
<div hidden></div>
d.getAttribute('hidden'); // => ''
d.hidden; // => true
let d = document.createElement('div');
d.setAttribute('hidden', ''...
<div id="wrapper" wrapper-id="main" hidden></div>
attribute and
property
attribute and
boolean property
attribute, no
prop...
<div id="wrapper" wrapper-id="main" hidden></div>
attribute and
property
attribute and
boolean property
attribute, no
prop...
<div id="wrapper" wrapper-id="main" hidden></div>
<select tabindex="3" multiple="" size="-4" form="login"></select>
1 <my-component count="3" running></my-component>
1 class MyComponent extends HTMLElement {
2 attachedCallback() {
3 conso...
Component consumer concerns
DOM
Angular 2 TemplateEmber Template
let c = document.createElement(
‘my-component'
);
c.setAttribute('count', '5');
<my-c...
DOM
Angular 2 TemplateEmber Template
<my-component count={count}>
</my-component>
<my-component count={{count}}>
</my-comp...
Back to author concerns
~~ Pattern #2 ~~
Pass attributes, then
deserialize them to
properties
Polymer: reflecting between an attribute and property
1 Polymer({
2 is: 'my-component',
3 properties: {
4 count: Number
5 ...
1 xtag.register('my-component', {
2 accessors: {
3 count: {
4 attribute: {},
5 get() {
6 let stringCount = this.getAttribu...
~~ Pattern #3 ~~
Communicate with
events
Consumer concerns
<AReactComponent log={function(){ console.log('baz'); }} />
{{an-ember-component log=(action 'log' 'baz')}}
Framework comp...
Custom Elements
<my-component log="function() { console.log('log') }"></my-component>
HTML Elements
div.setAttribute('onclick', ‘console.log("clicked")');
div.onclick = function() { console.log("clicked"); };...
Pass an attribute? A string of a function has
no context (this is lost)
Pass a property? No way to do this in HTML
and som...
Pass an attribute? A string of a function has
no context (this is lost)
Pass a property? No way to do this in HTML
and som...
~~ Pattern #3 ~~
Communicate with
events
Events are how HTML Elements communicate
so
Custom Elements should behave the same way
Why Events? #1
<div></div>
<my-component></my-component>
1 let event = document.createEvent('Event');
2 event.initEvent('click', true, tr...
Events have a somewhat common interface
Why Events? #2
Event Listener
1 element.addEventListener('click', function(e) {
2 e.target; // What element dispatched the event
3 e.curr...
<my-component></my-component>
1 let event = document.createEvent('CustomEvent');
2 event.initCustomEvent('mousewobble', tr...
Events are compatible with alternative
consumer patterns (delegation)
Why Events? #3
Component consumer concerns
Angular 2 Template
1 <my-component (mousewobble)="methodOnComponent($event)">
2 </my-component>
3
4 <my-component on-mouse...
~~ Pattern #3 ~~
Communicate with
events
~~ Pattern #1 ~~
Use Custom
Elements via a
polyfill
~~ Pattern #3 ~~
Communicate
with events
~~ Pattern #2 ~~
Pass attribu...
Practical recommendations for interoperable component authors
use x-tag or Polymer
Practical recommendations for interoperable component authors
use x-tag or Polymer
Practical recommendations for interoperable component authors
document usage in several environments
use x-tag or Polymer
Practical recommendations for interoperable component authors
document usage in several environments
...
use x-tag or Polymer
Practical recommendations for interoperable component authors
document usage in several environments
...
~~ Coda ~~
Ember
<my-component count="5"></my-component>
<my-component count="5"></my-component>
<my-component {{attribute count=5}}></my-component>
<my-component count="5"></my-component>
<my-component {{attribute count=5}}></my-component>
<my-component {{property count...
<my-component count="5"></my-component>
<my-component {{attribute count=5}}></my-component>
<my-component {{property count...
<my-component count="5"></my-component>
<my-component {{attribute count=5}}></my-component>
<my-component {{property count...
twitter.com/mixonic
Ember.js Core Team
madhatted.com
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Interoperable Component Patterns
Upcoming SlideShare
Loading in …5
×

Interoperable Component Patterns

584 views

Published on

Though component and custom element patterns have become the standard for web application architecture, communicating from one solution to another remains fogged by guesswork and opinion.

How should you write a web component that needs to be compatible with Ember and other frameworks? Where are the tradeoffs between purity of design and ergonomics? In a talk touching on libraries and standards, come learn how to write component code useful across many environments.

Published in: Technology
  • Be the first to comment

Interoperable Component Patterns

  1. 1. Interoperable Component Patterns EmberConf 2016
  2. 2. twitter.com/mixonic Ember.js Core Team madhatted.com
  3. 3. twitter.com/mixonic Ember.js Core Team madhatted.com
  4. 4. >1,800 people!!
  5. 5. emberjs.com/ember-community-survey-2016
  6. 6. emberjs.com/ember-community-survey-2016
  7. 7. “The elements of this language are entities called patterns. Each pattern describes a problem that occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.” –Christopher Alexander
  8. 8. User-centric
  9. 9. Designed based on experience
  10. 10. A pattern is a not a guide to implementation
  11. 11. ~~ Prelude ~~ What we talk about when we talk about interoperability. and web components
  12. 12. Rails server builds an HTML string Browser parses HTML string Component boots in the browser Possible Consumer #1
  13. 13. Rails server builds an HTML string Browser parses HTML string Component boots in the browser Possible Consumer #1
  14. 14. React template builds some DOM Component boots in the browser Component is implemented w/Vue.js Possible Consumer #2
  15. 15. Possible Consumer #2 React template builds some DOM Component boots in the browser Component is implemented w/Vue.js
  16. 16. Riot template is rendered Server-side Component boots on the server Component renders and resulting HTML goes back in the HTTP response Possible Consumer #3
  17. 17. Riot template is rendered Server-side Component boots on the server Component renders and resulting HTML goes back in the HTTP response Possible Consumer #3
  18. 18. A component usable from any client-side consumer.
  19. 19. A component usable from any client-side consumer.
  20. 20. Ember Template React/JSX Angular 1.x Template Angular 2 Template DOM HTML
  21. 21. ~~ Pattern #1 ~~ Use Custom Elements via a polyfill
  22. 22. document.createElement(
 'my-component'
 ); <my-component>
 </my-component> DOM HTML
  23. 23. Angular 2 TemplateEmber Template <my-component> </my-component> React/JSX Angular 1.x Template document.createElement(
 'my-component'
 ); <my-component>
 </my-component> DOM HTML <my-component>
 </my-component> <my-component>
 </my-component> <my-component>
 </my-component>
  24. 24. 1 class MyComponent extends HTMLElement { 2 attachedCallback() { 3 console.log('Now attached to the document'); 4 } 5 }; 6 document.registerElement('my-component', MyComponent); Custom Elements Working Draft, 26 February 2016
  25. 25. 1 class MyComponent extends HTMLElement { 2 attachedCallback() { 3 console.log('Now attached to the document'); 4 } 5 }; 6 window.customElements.define('my-component', MyComponen Custom Elements Working Draft, 29 March 2016
  26. 26. webcomponents.org/polyfills/custom-elements x-tag.github.io www.polymer-project.org
  27. 27. Angular 2 TemplateEmber Template React/JSX Angular 1.x Template DOM HTML <my-component> </my-component> document.createElement(
 'my-component'
 ); <my-component>
 </my-component> <my-component>
 </my-component> <my-component>
 </my-component> <my-component>
 </my-component>
  28. 28. ~~ Pattern #2 ~~ Pass attributes, then deserialize them to properties
  29. 29. ~~ Pattern #2 ~~ Pass attributes, then deserialize them to properties on change
  30. 30. Component author concerns
  31. 31. <div id="wrapper"></div>
  32. 32. <div id="wrapper"></div> let d = document.createElement('div'); d.setAttribute('id', 'wrapper');
  33. 33. <div id="wrapper"></div> let d = document.createElement('div'); d.setAttribute('id', 'wrapper'); d.getAttribute('id'); // => 'wrapper' d.id; // => 'wrapper'
  34. 34. let d = document.createElement('div'); d.setAttribute('wrapper-id', 'main'); <div wrapper-id="main"></div>
  35. 35. let d = document.createElement('div'); d.setAttribute('wrapper-id', 'main'); d.getAttribute('wrapper-id'); // => 'main' d['wrapper-id']; // => undefined <div wrapper-id="main"></div>
  36. 36. let d = document.createElement('div'); d.setAttribute('hidden', ''); <div hidden></div>
  37. 37. d.getAttribute('hidden'); // => '' d.hidden; // => true let d = document.createElement('div'); d.setAttribute('hidden', ''); <div hidden></div>
  38. 38. <div id="wrapper" wrapper-id="main" hidden></div> attribute and property attribute and boolean property attribute, no property
  39. 39. <div id="wrapper" wrapper-id="main" hidden></div> attribute and property attribute and boolean property attribute, no property
  40. 40. <div id="wrapper" wrapper-id="main" hidden></div> <select tabindex="3" multiple="" size="-4" form="login"></select>
  41. 41. 1 <my-component count="3" running></my-component> 1 class MyComponent extends HTMLElement { 2 attachedCallback() { 3 console.log('Now attached to the document'); 4 } 5 }; 6 window.customElements.define('my-component', MyComponen
  42. 42. Component consumer concerns
  43. 43. DOM Angular 2 TemplateEmber Template let c = document.createElement( ‘my-component' ); c.setAttribute('count', '5'); <my-component count=“5"> </my-component> <my-component count=“5"> </my-component> <my-component count=“5"> </my-component> <my-component count=“5"> </my-component> <my-component count=“5"> </my-component> React/JSX Angular 1.x TemplateHTML
  44. 44. DOM Angular 2 TemplateEmber Template <my-component count={count}> </my-component> <my-component count={{count}}> </my-component> <my-component count={{count}}> </my-component> <my-component [attr.count]="coun </my-component> React/JSX Angular 1.x TemplateHTML let c = document.createElement( ‘my-component' ); c.setAttribute('count', '5'); <my-component count=“5"> </my-component>
  45. 45. Back to author concerns
  46. 46. ~~ Pattern #2 ~~ Pass attributes, then deserialize them to properties
  47. 47. Polymer: reflecting between an attribute and property 1 Polymer({ 2 is: 'my-component', 3 properties: { 4 count: Number 5 } 6 });
  48. 48. 1 xtag.register('my-component', { 2 accessors: { 3 count: { 4 attribute: {}, 5 get() { 6 let stringCount = this.getAttribute('count'); 7 return parseInt(stringCount, 10); 8 }, 9 set(numericValue) { 10 let value = ''+numericValue; 11 this.setAttribute('count', value); 12 return value; 13 } 14 } 15 } 16 }); x-tag: reflecting between an attribute and property
  49. 49. ~~ Pattern #3 ~~ Communicate with events
  50. 50. Consumer concerns
  51. 51. <AReactComponent log={function(){ console.log('baz'); }} /> {{an-ember-component log=(action 'log' 'baz')}} Framework components
  52. 52. Custom Elements <my-component log="function() { console.log('log') }"></my-component>
  53. 53. HTML Elements div.setAttribute('onclick', ‘console.log("clicked")'); div.onclick = function() { console.log("clicked"); }; div.addEventListener('click', function(e){ window.event = e; try { div.onclick.call(event.target); } finaly() { window.event = null; } } <div onclick="console.log('clicked')"></div>
  54. 54. Pass an attribute? A string of a function has no context (this is lost) Pass a property? No way to do this in HTML and some frameworks Add an event listener? No way to add an event listener in HTML
  55. 55. Pass an attribute? A string of a function has no context (this is lost) Pass a property? No way to do this in HTML and some frameworks Add an event listener? No way to add an event listener in HTML
  56. 56. ~~ Pattern #3 ~~ Communicate with events
  57. 57. Events are how HTML Elements communicate so Custom Elements should behave the same way Why Events? #1
  58. 58. <div></div> <my-component></my-component> 1 let event = document.createEvent('Event'); 2 event.initEvent('click', true, true); // canBubble, cancelable 3 element.dispatchEvent(event); 1 let event = document.createEvent('Event'); 2 event.initEvent('click', true, true); // canBubble, cancelable 3 element.dispatchEvent(event);
  59. 59. Events have a somewhat common interface Why Events? #2
  60. 60. Event Listener 1 element.addEventListener('click', function(e) { 2 e.target; // What element dispatched the event 3 e.currentTarget; // What element the handler is attached to 4 e.timeStamp; // When the event started 5 // Plus properties and methods about phases 6 }); 1 element.callbackFunction = function() { 2 // What are the arguments this time? 3 }; Callback as property
  61. 61. <my-component></my-component> 1 let event = document.createEvent('CustomEvent'); 2 event.initCustomEvent('mousewobble', true, true, {direction: 'vertical'}); 3 element.dispatchEvent(event); 1 element.addEventListener('mousewobble', function(e) { 2 e.detail.direction; // 'vertical' 3 });
  62. 62. Events are compatible with alternative consumer patterns (delegation) Why Events? #3
  63. 63. Component consumer concerns
  64. 64. Angular 2 Template 1 <my-component (mousewobble)="methodOnComponent($event)"> 2 </my-component> 3 4 <my-component on-mousewobble="methodOnComponent($event)"> 5 </my-component>
  65. 65. ~~ Pattern #3 ~~ Communicate with events
  66. 66. ~~ Pattern #1 ~~ Use Custom Elements via a polyfill ~~ Pattern #3 ~~ Communicate with events ~~ Pattern #2 ~~ Pass attributes, then deserialize them to properties
  67. 67. Practical recommendations for interoperable component authors
  68. 68. use x-tag or Polymer Practical recommendations for interoperable component authors
  69. 69. use x-tag or Polymer Practical recommendations for interoperable component authors document usage in several environments
  70. 70. use x-tag or Polymer Practical recommendations for interoperable component authors document usage in several environments punt on Shadow DOM
  71. 71. use x-tag or Polymer Practical recommendations for interoperable component authors document usage in several environments punt on Shadow DOM don’t expose implementation details
  72. 72. ~~ Coda ~~ Ember
  73. 73. <my-component count="5"></my-component>
  74. 74. <my-component count="5"></my-component> <my-component {{attribute count=5}}></my-component>
  75. 75. <my-component count="5"></my-component> <my-component {{attribute count=5}}></my-component> <my-component {{property count=5}}></my-component>
  76. 76. <my-component count="5"></my-component> <my-component {{attribute count=5}}></my-component> <my-component {{property count=5}}></my-component> <my-component {{style color="red" width=(max a b)}}></my-component>
  77. 77. <my-component count="5"></my-component> <my-component {{attribute count=5}}></my-component> <my-component {{property count=5}}></my-component> <my-component {{style color="red" width=(max a b)}}></my-component> <my-component {{add-event-listener 'mousewobble' (action 'wobble')}}></my-component>
  78. 78. twitter.com/mixonic Ember.js Core Team madhatted.com

×