New Component
Patterns in Ember 1.10+
or, You Really Can Take it With You
I’m Matthew
@mixonic - madhatted.com
201 Created
Ember.js experts and JavaScript superheroes in NYC
Out with the old,

in with the new.
1 // app/index/controller.js
2 import Ember from "ember";
3
4 export Ember.Controller.extend({
5 session: Ember.inject.service();
6 });
1 // app/services/session.js
2 import Ember from "ember";
3
4 export Ember.Object.extend({
5 isAuthenticated: false,
6 login: function() {
7 // ...
8 }
9 });
Services in 1.10!
HTMLBars dep! debug file!
1 <script src="/jquery.js"></script>
2 <script src="/handlebars-v2.0.0.js"></script>
3 <script src=“/1.9.1/ember.js”></script>
1 <script src="/jquery.js"></script>
2 <script src=“/1.10.0/ember-template-compiler.js”></script>
3 <script src=“/1.10.0/ember.debug.js”></script>
1.9
1.10
HTMLBars!
Love me!
New APIs mean new
development patterns.
Out with the scopes,

in with the variables.
I
Context switching deprecated in 1.9
1 {{! this context is the controller }}
2 {{#each model}}
3 {{name}} {{! this context is each person }}
4 {{/each}}
1 {{! this context is the controller }}
2 {{#each person in model}}
3 {{person.name}} {{! this context is still the controller }}
4 {{/each}}
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
Context switching deprecated in 1.9
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
1 {{#if config.pro}}
2 <div class="travis-lint">
3 <p>Travis Lint for clean .yml files
4 <a {{bind-attr href="lintUrl"}}>lint.travis-ci.org/{{repo.slug}}</a>
5 </p>
6 </div>
7 {{/if}}
8
9 <table id="requests" class="list">
10 <thead>
11 <tr>
12 <th>Request</th>
13 <th>Commit</th>
14 <th>Build</th>
15 <th>Commit message</th>
16 <th>Type</th>
17 <th>Message</th>
18 </tr>
19 </thead>
20
21 <tbody>
22 {{#each controller itemController="request"}}
23 <tr {{bind-attr class="requestClass"}}>
24 <td class="request-id">
25 <span class="status"></span>
26 {{id}}
27 </td>
28 <td>{{githubCommitLink repo.slug commit.sha}}</td>
29 <td>
30 {{#if build}}
31 {{#link-to "build" build}}#{{build.number}}{{/link-to}}
32 {{else}}
33 -
34 {{/if}}
35 </td>
36 <td class="commit-message">{{{formatMessage commit.message short="true" repoBinding=build.repo}}}</td>
37 <td>{{type}}</td>
38 <td>{{message}}</td>
39 </tr>
40 {{/each}}
41 </tbody>
42 </table>
Context switching deprecated in 1.9
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
1 {{#if config.pro}}
2 <div class="travis-lint">
3 <p>Travis Lint for clean .yml files
4 <a {{bind-attr href="lintUrl"}}>lint.travis-ci.org/{{repo.slug}}</a>
5 </p>
6 </div>
7 {{/if}}
8
9 <table id="requests" class="list">
10 <thead>
11 <tr>
12 <th>Request</th>
13 <th>Commit</th>
14 <th>Build</th>
15 <th>Commit message</th>
16 <th>Type</th>
17 <th>Message</th>
18 </tr>
19 </thead>
20
21 <tbody>
22 {{#each controller itemController="request"}}
23 <tr {{bind-attr class="requestClass"}}>
24 <td class="request-id">
25 <span class="status"></span>
26 {{id}}
27 </td>
28 <td>{{githubCommitLink repo.slug commit.sha}}</td>
29 <td>
30 {{#if build}}
31 {{#link-to "build" build}}#{{build.number}}{{/link-to}}
32 {{else}}
33 -
34 {{/if}}
35 </td>
36 <td class="commit-message">{{{formatMessage commit.message short="true" repoBinding=build.repo}}}</td>
37 <td>{{type}}</td>
38 <td>{{message}}</td>
39 </tr>
40 {{/each}}
41 </tbody>
42 </table>
New scope
1 <ul id="queues">
2 <li class="queue">
3 <h4>Queue ({{controller.length}})</h4>
4 <ul>
5 {{#if controller.length}}
6 {{#each job in controller}}
7 {{#view Travis.QueueItemView jobBinding="job"}}
8 {{#if job.repo.slug}}
9 {{#link-to "job" job.repo job}}
10 <span class="slug" {{bind-attr title="job.slug"}}>
11 {{job.repo.slug}}
12 </span>
13 #{{job.number}}
14 {{/link-to}}
15 {{/if}}
16 {{/view}}
17 {{/each}}
18 {{else}}
19 There are no jobs
20 {{/if}}
21 </ul>
22 </li>
23 </ul>
Context switching deprecated in 1.9
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
Possible new scope?!
Passing variables is easier
to reason about than
changing scope.
Ember 1.10 introduces
“block params”
1 {{! this context is the controller }}
2 {{#each person in model}}
3 {{person.name}} {{! this context is still the controller }}
4 {{/each}}
Block params introduced in 1.10
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
1 {{! this context is the controller }}
2 {{#each model as |person|}}
3 {{person.name}} {{! this context is still the controller }}
4 {{/each}}
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
1 {{#x-menu}}
2 {{x-item selected=selected item=item}}
3 {{/x-menu}}
Block params introduced in 1.10
1 <div class="x-menu">
2 {{yield}}
3 </div>
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
1 {{#x-menu as |selected|}}
2 {{x-item selected=selected item=item}}
3 {{/x-menu}}
Block params introduced in 1.10
1 <div class="x-menu">
2 {{yield selected}}
3 </div>
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
1 {{#x-menu as |selected item|}}
2 {{x-item selected=selected item=item}}
3 {{/x-menu}}
Block params introduced in 1.10
1 <div class="x-menu">
2 {{yield selected item}}
3 </div>
1 <div class="x-menu">
2 {{#each items as |item|}}
3 {{yield selected item}}
4 {{/each}}
5 </div>
http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
1 {{#x-menu as |selected item|}}
2 {{x-item selected=selected item=item}}
3 {{/x-menu}}
Block params introduced in 1.10
Out with the controllers,

in with the components.
II
Out with the views,

in with the components.
II
Controllers Views
Receive actions Handle events
Provide template scope Describe elements
1 export default Ember.Component.extend({
2 actions: {
3 save: function(model){
4 model.save();
5 }
6 }
7 });
1 <button {{action "save" model}}>Save</button>
Controllers Views
Receive actions Handle events
Provide template scope Describe elements
1 export default Ember.Component.extend({
2 color: 'red'
3 });
1 <div>{{color}}</div>
Controllers Views
Receive actions Handle events
Provide template scope Describe elements
1 export default Ember.Component.extend({
2 click: function(){
3 this.sendAction('save');
4 }
5 });
Controllers Views
Receive actions Handle events
Provide template scope Describe elements
1 export default Ember.Component.extend({
2 tagName: 'li',
3 classNames: ['red']
4 });
Controllers deprecated when 2.0 comes out
“Many people have noticed that
controllers in Ember look a lot like
components, but with an arbitrary division
of responsibilities. We agree!”
- Tomkatz, 2.0 RFC
https://github.com/emberjs/rfcs/pull/15
For new users, a single
concept is easier to learn.
“But I need mega-hackz!”
III
I CAN HAZ
MEGA

HACKZ
Components don’t yet
handle every use-case
We’re working on it!
Here are some ideas
Not finished, polished, or maybe even ever going to happen.
On Ember components that use the new tag
syntax, data-binding will be one-way. The mut
helper will wrap values that the receiver can
modify.
<x-input value={{mut name}}></x-input>
1 export default Ember.Component.extend({!
2 tagName: 'input',!
3 change: function(){!
4 this.attrs.name.set(this.$().val());!
5 }!
6 });!
bind-action, or maybe action, will wrap the
action it references in a closure, and pass it as
a function. You can call it directly.
{{x-input model=model submit=(bind-action "save")}}
1 export default Ember.Component.extend({
2 change: function(){
3 this.attrs.submit(model);
4 }
5 });
ref would allow you to point an action at a
specific component in your current template.
1 {{video-player ref="vp"}}
2
3 <button on-click={{ref 'vp' 'play'}}>
set would allow you to easily map an
event to a value setter.
<x-input on-change={{set model.name}}></x-input>
scoped helpers allow a component to yield
references to components or helpers.
1 // app/components/form-for.js
2
3 import Ember from "ember";
4 import FormForInput from "./helpers/input";
5
6 var makeViewHelper = Ember.HTMLBars.makeViewHelper;
7
8 export default Ember.Component.extend({
9 formHelpers: {
10 input: makeViewHelper(FormForInput)
11 }
12 });
1 {{! app/templates/components/form-for.hbs }}
2
3 {{yield formHelpers}}
1 {{! app/templates/post/new.hbs }}
2
3 {{#form-for as |f|}}
4 {{f.input label="Title" value=model.title}}
5 {{/form-for}}
Let’s cowboy code
some prototypes
bit.ly/cowboy-code
BEWARE
An experiment with
mut and bind-action
bit.ly/cowboy-1
Using today’s tools
bit.ly/cowboy-2 block params
bit.ly/cowboy-3 bind-action
bit.ly/cowboy-4 more bind-action
bit.ly/cowboy-5 pass bind-action as block param
bit.ly/cowboy-6 yield with each
bit.ly/cowboy-7 mut
Start using block params
today, and you will quickly
want some of these tools.
Don’t let it stop you!
Thanks!
Questions?

New Component Patterns in Ember.js

  • 1.
    New Component Patterns inEmber 1.10+ or, You Really Can Take it With You
  • 2.
  • 3.
    201 Created Ember.js expertsand JavaScript superheroes in NYC
  • 4.
    Out with theold,
 in with the new.
  • 5.
    1 // app/index/controller.js 2import Ember from "ember"; 3 4 export Ember.Controller.extend({ 5 session: Ember.inject.service(); 6 }); 1 // app/services/session.js 2 import Ember from "ember"; 3 4 export Ember.Object.extend({ 5 isAuthenticated: false, 6 login: function() { 7 // ... 8 } 9 }); Services in 1.10!
  • 6.
    HTMLBars dep! debugfile! 1 <script src="/jquery.js"></script> 2 <script src="/handlebars-v2.0.0.js"></script> 3 <script src=“/1.9.1/ember.js”></script> 1 <script src="/jquery.js"></script> 2 <script src=“/1.10.0/ember-template-compiler.js”></script> 3 <script src=“/1.10.0/ember.debug.js”></script> 1.9 1.10
  • 7.
  • 8.
    New APIs meannew development patterns.
  • 9.
    Out with thescopes,
 in with the variables. I
  • 10.
    Context switching deprecatedin 1.9 1 {{! this context is the controller }} 2 {{#each model}} 3 {{name}} {{! this context is each person }} 4 {{/each}} 1 {{! this context is the controller }} 2 {{#each person in model}} 3 {{person.name}} {{! this context is still the controller }} 4 {{/each}} http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html
  • 11.
    Context switching deprecatedin 1.9 http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html 1 {{#if config.pro}} 2 <div class="travis-lint"> 3 <p>Travis Lint for clean .yml files 4 <a {{bind-attr href="lintUrl"}}>lint.travis-ci.org/{{repo.slug}}</a> 5 </p> 6 </div> 7 {{/if}} 8 9 <table id="requests" class="list"> 10 <thead> 11 <tr> 12 <th>Request</th> 13 <th>Commit</th> 14 <th>Build</th> 15 <th>Commit message</th> 16 <th>Type</th> 17 <th>Message</th> 18 </tr> 19 </thead> 20 21 <tbody> 22 {{#each controller itemController="request"}} 23 <tr {{bind-attr class="requestClass"}}> 24 <td class="request-id"> 25 <span class="status"></span> 26 {{id}} 27 </td> 28 <td>{{githubCommitLink repo.slug commit.sha}}</td> 29 <td> 30 {{#if build}} 31 {{#link-to "build" build}}#{{build.number}}{{/link-to}} 32 {{else}} 33 - 34 {{/if}} 35 </td> 36 <td class="commit-message">{{{formatMessage commit.message short="true" repoBinding=build.repo}}}</td> 37 <td>{{type}}</td> 38 <td>{{message}}</td> 39 </tr> 40 {{/each}} 41 </tbody> 42 </table>
  • 12.
    Context switching deprecatedin 1.9 http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html 1 {{#if config.pro}} 2 <div class="travis-lint"> 3 <p>Travis Lint for clean .yml files 4 <a {{bind-attr href="lintUrl"}}>lint.travis-ci.org/{{repo.slug}}</a> 5 </p> 6 </div> 7 {{/if}} 8 9 <table id="requests" class="list"> 10 <thead> 11 <tr> 12 <th>Request</th> 13 <th>Commit</th> 14 <th>Build</th> 15 <th>Commit message</th> 16 <th>Type</th> 17 <th>Message</th> 18 </tr> 19 </thead> 20 21 <tbody> 22 {{#each controller itemController="request"}} 23 <tr {{bind-attr class="requestClass"}}> 24 <td class="request-id"> 25 <span class="status"></span> 26 {{id}} 27 </td> 28 <td>{{githubCommitLink repo.slug commit.sha}}</td> 29 <td> 30 {{#if build}} 31 {{#link-to "build" build}}#{{build.number}}{{/link-to}} 32 {{else}} 33 - 34 {{/if}} 35 </td> 36 <td class="commit-message">{{{formatMessage commit.message short="true" repoBinding=build.repo}}}</td> 37 <td>{{type}}</td> 38 <td>{{message}}</td> 39 </tr> 40 {{/each}} 41 </tbody> 42 </table> New scope
  • 13.
    1 <ul id="queues"> 2<li class="queue"> 3 <h4>Queue ({{controller.length}})</h4> 4 <ul> 5 {{#if controller.length}} 6 {{#each job in controller}} 7 {{#view Travis.QueueItemView jobBinding="job"}} 8 {{#if job.repo.slug}} 9 {{#link-to "job" job.repo job}} 10 <span class="slug" {{bind-attr title="job.slug"}}> 11 {{job.repo.slug}} 12 </span> 13 #{{job.number}} 14 {{/link-to}} 15 {{/if}} 16 {{/view}} 17 {{/each}} 18 {{else}} 19 There are no jobs 20 {{/if}} 21 </ul> 22 </li> 23 </ul> Context switching deprecated in 1.9 http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html Possible new scope?!
  • 14.
    Passing variables iseasier to reason about than changing scope.
  • 15.
  • 16.
    1 {{! thiscontext is the controller }} 2 {{#each person in model}} 3 {{person.name}} {{! this context is still the controller }} 4 {{/each}} Block params introduced in 1.10 http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html 1 {{! this context is the controller }} 2 {{#each model as |person|}} 3 {{person.name}} {{! this context is still the controller }} 4 {{/each}}
  • 17.
    http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html 1 {{#x-menu}} 2 {{x-itemselected=selected item=item}} 3 {{/x-menu}} Block params introduced in 1.10 1 <div class="x-menu"> 2 {{yield}} 3 </div>
  • 18.
    http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html 1 {{#x-menu as|selected|}} 2 {{x-item selected=selected item=item}} 3 {{/x-menu}} Block params introduced in 1.10 1 <div class="x-menu"> 2 {{yield selected}} 3 </div>
  • 19.
    http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html 1 {{#x-menu as|selected item|}} 2 {{x-item selected=selected item=item}} 3 {{/x-menu}} Block params introduced in 1.10 1 <div class="x-menu"> 2 {{yield selected item}} 3 </div>
  • 20.
    1 <div class="x-menu"> 2{{#each items as |item|}} 3 {{yield selected item}} 4 {{/each}} 5 </div> http://emberjs.com/blog/2014/12/08/ember-1-9-0-released.html 1 {{#x-menu as |selected item|}} 2 {{x-item selected=selected item=item}} 3 {{/x-menu}} Block params introduced in 1.10
  • 21.
    Out with thecontrollers,
 in with the components. II
  • 22.
    Out with theviews,
 in with the components. II
  • 24.
    Controllers Views Receive actionsHandle events Provide template scope Describe elements 1 export default Ember.Component.extend({ 2 actions: { 3 save: function(model){ 4 model.save(); 5 } 6 } 7 }); 1 <button {{action "save" model}}>Save</button>
  • 25.
    Controllers Views Receive actionsHandle events Provide template scope Describe elements 1 export default Ember.Component.extend({ 2 color: 'red' 3 }); 1 <div>{{color}}</div>
  • 26.
    Controllers Views Receive actionsHandle events Provide template scope Describe elements 1 export default Ember.Component.extend({ 2 click: function(){ 3 this.sendAction('save'); 4 } 5 });
  • 27.
    Controllers Views Receive actionsHandle events Provide template scope Describe elements 1 export default Ember.Component.extend({ 2 tagName: 'li', 3 classNames: ['red'] 4 });
  • 28.
    Controllers deprecated when2.0 comes out “Many people have noticed that controllers in Ember look a lot like components, but with an arbitrary division of responsibilities. We agree!” - Tomkatz, 2.0 RFC https://github.com/emberjs/rfcs/pull/15
  • 29.
    For new users,a single concept is easier to learn.
  • 30.
    “But I needmega-hackz!” III
  • 31.
  • 32.
  • 33.
  • 34.
    Here are someideas Not finished, polished, or maybe even ever going to happen.
  • 35.
    On Ember componentsthat use the new tag syntax, data-binding will be one-way. The mut helper will wrap values that the receiver can modify. <x-input value={{mut name}}></x-input> 1 export default Ember.Component.extend({! 2 tagName: 'input',! 3 change: function(){! 4 this.attrs.name.set(this.$().val());! 5 }! 6 });!
  • 36.
    bind-action, or maybeaction, will wrap the action it references in a closure, and pass it as a function. You can call it directly. {{x-input model=model submit=(bind-action "save")}} 1 export default Ember.Component.extend({ 2 change: function(){ 3 this.attrs.submit(model); 4 } 5 });
  • 37.
    ref would allowyou to point an action at a specific component in your current template. 1 {{video-player ref="vp"}} 2 3 <button on-click={{ref 'vp' 'play'}}>
  • 38.
    set would allowyou to easily map an event to a value setter. <x-input on-change={{set model.name}}></x-input>
  • 39.
    scoped helpers allowa component to yield references to components or helpers. 1 // app/components/form-for.js 2 3 import Ember from "ember"; 4 import FormForInput from "./helpers/input"; 5 6 var makeViewHelper = Ember.HTMLBars.makeViewHelper; 7 8 export default Ember.Component.extend({ 9 formHelpers: { 10 input: makeViewHelper(FormForInput) 11 } 12 }); 1 {{! app/templates/components/form-for.hbs }} 2 3 {{yield formHelpers}} 1 {{! app/templates/post/new.hbs }} 2 3 {{#form-for as |f|}} 4 {{f.input label="Title" value=model.title}} 5 {{/form-for}}
  • 40.
  • 41.
  • 42.
    An experiment with mutand bind-action
  • 48.
  • 49.
    bit.ly/cowboy-2 block params bit.ly/cowboy-3bind-action bit.ly/cowboy-4 more bind-action bit.ly/cowboy-5 pass bind-action as block param bit.ly/cowboy-6 yield with each bit.ly/cowboy-7 mut
  • 50.
    Start using blockparams today, and you will quickly want some of these tools. Don’t let it stop you!
  • 51.