Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
EmberJS Amsterdam - Closure Actions
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Marten
March 10, 2016
Programming
120
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
EmberJS Amsterdam - Closure Actions
Marten
March 10, 2016
More Decks by Marten
See All by Marten
Ember Service Worker - EmberFest Edition
martndemus
5
610
Ember.Object
martndemus
0
86
Other Decks in Programming
See All in Programming
「正の参照」と 「負の導出」で組む ハーネスエンジニアリング
cottpan
1
140
5分で問診!Composer セキュリティ健康診断
codmoninc
0
370
エンジニアにデザインハーネスを 〜デザインプロセスを規定するためのハーネス〜 / Design harness from an engineer's perspective
rkaga
2
1.5k
初めてのKubernetes 本番運用でハマった話
oku053
0
130
どこまでゆるくて許されるのか
tk3fftk
0
500
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
220
20260623_Loop Engineeringで自分の分身の問い合わせBotを作る
ryugen04
0
220
ランチタイムLT会3周年!ランチタイムLT会を3年間続けられたお話
y0hgi
1
140
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
170
分散システム、なんですぐ死んでしまうん?耐障害性を高めたいあなたのためのレジリエンスパターン入門
mshibuya
7
6.4k
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
130
1B+ /day規模のログを管理する技術
broadleaf
0
140
Featured
See All Featured
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
360
How STYLIGHT went responsive
nonsquared
100
6.2k
Making the Leap to Tech Lead
cromwellryan
135
10k
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Evolving SEO for Evolving Search Engines
ryanjones
0
240
Balancing Empowerment & Direction
lara
6
1.2k
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
220
Visualization
eitanlees
152
17k
How to Ace a Technical Interview
jacobian
281
24k
How to build a perfect <img>
jonoalderson
1
5.8k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
350
Transcript
CLOSURE ACTIONS EMBER.JS
MARTEN SCHILSTRA @martndemus HELLO MY NAME IS:
I WORK FOR:
CLOSURE ACTIONS EMBER.JS
CLASSIC ACTIONS BEFORE CLOSURE ACTIONS
<button {{action “submit”}}> Submit </button> BINDING ACTIONS TO HTML ELEMENTS
<button {{action “submit”}}> Submit </button> BINDING ACTIONS TO HTML ELEMENTS
<button {{action “submit”}}> Submit </button> BINDING ACTIONS TO HTML ELEMENTS
element action keyword
<form {{action “submit” on=“submit”}}> <!—— form fields ——> <input type=“submit”
/> </form> BINDING ACTIONS TO HTML ELEMENTS
{{#x-button action=“save”}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS
{{#x-button action=“save”}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS
{{#x-button action=“save”}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS an attribute
actions: { submit() { this.sendAction(); } } CALLING ACTIONS FROM
YOUR COMPONENT
actions: { submit() { this.sendAction(); } } CALLING ACTIONS FROM
YOUR COMPONENT
actions: { submit() { this.sendAction(); } } CALLING ACTIONS FROM
YOUR COMPONENT actions hash
actions: { submit() { this.sendAction(); } } CALLING ACTIONS FROM
YOUR COMPONENT
actions: { submit() { this.sendAction(); } } CALLING ACTIONS FROM
YOUR COMPONENT
actions: { saveModel() { // saves model }, submit()
{ this.send(‘saveModel’); } } CALLING ACTIONS FROM YOUR COMPONENT
actions: { saveModel() { // saves model }, submit()
{ this.send(‘saveModel’); } } CALLING ACTIONS FROM YOUR COMPONENT
LOOKS GOOD
LOOKS GOOD, RIGHT?
BUBBLING UP COMPONENTS
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
// controllers/login.js actions: { login(username, password) { // logic }
} {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
// components/login-form.js actions: { submit() { this.sendAction(‘login’, get(this, ‘username’), get(this,
‘password’)); } } {{! templates/components/login-form.hbs }} {{#x-button action=“submit”}} Login {{/x-button}} COMPONENTS/LOGIN-FORM
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
// components/x-button.js actions: { click() { this.sendAction(); } } {{!
templates/components/x-button.hbs }} <button {{action “click”}}> {{yield}} </button> COMPONENTS/X-BUTTON
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
// components/x-button.js actions: { click() { this.sendAction(); } } {{!
templates/components/x-button.hbs }} <button {{action “click”}}> {{yield}} </button> COMPONENTS/X-BUTTON
// components/x-button.js actions: { click() { this.sendAction(); } } {{!
templates/components/x-button.hbs }} <button {{action “click”}}> {{yield}} </button> COMPONENTS/X-BUTTON
// components/x-button.js actions: { click() { this.sendAction(); } } {{!
templates/components/x-button.hbs }} <button {{action “click”}}> {{yield}} </button> COMPONENTS/X-BUTTON
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
// components/login-form.js actions: { submit() { this.sendAction(‘login’, get(this, ‘username’), get(this,
‘password’)); } } {{! templates/components/login-form.hbs }} {{#x-button action=“submit”}} Login {{/x-button}} COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { this.sendAction(‘login’, get(this, ‘username’), get(this,
‘password’)); } } {{! templates/components/login-form.hbs }} {{#x-button action=“submit”}} Login {{/x-button}} COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { this.sendAction(‘login’, get(this, ‘username’), get(this,
‘password’)); } } {{! templates/components/login-form.hbs }} {{#x-button action=“submit”}} Login {{/x-button}} COMPONENTS/LOGIN-FORM
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
// controllers/login.js actions: { login(username, password) { // logic }
} {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password) { // logic }
} {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
{{login-form login=“login”}} {{x-button action=“submit”}} <button {{action “click”}}> login components/login-form components/x-button
BUBBLING UP COMPONENTS
RETURN VALUES
{{login-form login=“login”}} <button {{action=“submit”}}> login components/login-form RETURN VALUES
{{login-form login=“login”}} login components/login-form RETURN VALUES <button {{action=“submit”}}>
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button {{action “submit”}}>Login</button> {{/if}} COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button {{action “submit”}}>Login</button> {{/if}} COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form action=“login”}} LOGIN
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form action=“login”}} LOGIN
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button {{action “submit”}}>Login</button> {{/if}} COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button {{action “submit”}}>Login</button> {{/if}} COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} {{#x-button action=“submit”}} Login {{/x-button}} {{/if}} COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} {{#x-button action=“submit”}} Login {{/x-button}} {{/if}} COMPONENTS/LOGIN-FORM
{{login-form login=“login”}} login components/login-form RETURN VALUES user <button {{action=“submit”}}>
CLOSURE ACTIONS INTRODUCING
{{! before}} <button {{action “submit”}}> Submit </button> BINDING ACTIONS TO
HTML ELEMENTS
{{! before}} <button {{action “submit”}}> Submit </button> {{! with closure
actions}} <button onclick={{action “submit”}}> Submit </button> BINDING ACTIONS TO HTML ELEMENTS
<button onclick={{action “submit”}}> Submit </button> BINDING ACTIONS TO HTML ELEMENTS
<button onclick={{action “submit”}}> Submit </button> BINDING ACTIONS TO HTML ELEMENTS
<button onclick={{action “submit”}}> Submit </button> BINDING ACTIONS TO HTML ELEMENTS
closure action keyword
{{! before}} <form {{action “submit” on=“submit”}}> <!—— form fields ——>
</form> BINDING ACTIONS TO HTML ELEMENTS
{{! before}} <form {{action “submit” on=“submit”}}> <!—— form fields ——>
</form> {{! with closure actions}} <form onsubmit={{action “submit”}}> <!—— form fields ——> </form> BINDING ACTIONS TO HTML ELEMENTS
<form onsubmit={{action “submit”}}> <!—— form fields ——> </form> BINDING ACTIONS
TO HTML ELEMENTS
<form onsubmit={{action “submit”}}> <!—— form fields ——> </form> BINDING ACTIONS
TO HTML ELEMENTS
<form onsubmit={{action “submit”}}> <!—— form fields ——> </form> BINDING ACTIONS
TO HTML ELEMENTS event attribute on element
{{! before}} {{#x-button action=“submit”}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS
{{! before}} {{#x-button action=“submit”}} Submit {{/x-button}} {{! with closure actions}}
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS closure
action keyword
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}}
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}}
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}}
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}} no quotes!
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}}
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}} action called from actions hash
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}}
{{#x-button click=(action “submit”)}} Submit {{/x-button}} BINDING ACTIONS TO COMPONENTS {{#x-button
click=(action submit)}} Submit {{/x-button}} action called from component attributes
// before actions: { submit() { this.sendAction(‘submit’); } } CALLING
ACTIONS FROM YOUR COMPONENT
// before actions: { submit() { this.sendAction(‘submit’); } } //
with closure actions actions: { submit() { get(this, ‘submit’)(); } } CALLING ACTIONS FROM YOUR COMPONENT
actions: { submit() { get(this, ‘submit’)(); } } CALLING ACTIONS
FROM YOUR COMPONENT
actions: { submit() { get(this, ‘submit’)(); } } CALLING ACTIONS
FROM YOUR COMPONENT
actions: { submit() { get(this, ‘submit’)(); } } CALLING ACTIONS
FROM YOUR COMPONENT
I THOUGHT YOU SHOULD USE this.attrs.submit();
NO YOU SHOULDN'T locks.svbtle.com/to-attrs-or-not-to-attrs by Ricardo “locks” Mendes
CLOSURE ACTIONS DEEP-DIVE INTO
CLOSURE ACTIONS ACTION PASSING CURRYING RETURN VALUES FIRST ARGUMENT VALUE
OVERRIDING FUNCTIONS
CLOSURE ACTIONS ACTION PASSING CURRYING RETURN VALUES FIRST ARGUMENT VALUE
OVERRIDING FUNCTIONS
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
// controllers/items.js actions: { saveItem(item) { item.save(); } } ITEMS
{{! templates/items.hbs {{item-list saveItem=(action “saveItem”)}}
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{! templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item saveItem=(action saveItem)}}
{{/each}} COMPONENTS/ITEM-LIST
{{! templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item saveItem=(action saveItem)}}
{{/each}} COMPONENTS/ITEM-LIST no quotes!
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{! templates/components/item-detail.hbs <button onclick={{action saveItem item}}> Save </button> COMPONENTS/ITEM-DETAIL
{{! templates/components/item-detail.hbs <button onclick={{action saveItem item}}> Save </button> COMPONENTS/ITEM-DETAIL again
no quotes!
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{! templates/components/item-detail.hbs <button onclick={{action saveItem item}}> Save </button> COMPONENTS/ITEM-DETAIL
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{! templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item saveItem=(action saveItem)}}
{{/each}} COMPONENTS/ITEM-LIST
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
// controllers/items.js actions: { saveItem(item) { item.save(); } } ITEMS
{{! templates/items.hbs {{item-list saveItem=(action “saveItem”)}}
// controllers/items.js actions: { saveItem(item) { item.save(); } } ITEMS
{{! templates/items.hbs {{item-list saveItem=(action “saveItem”)}}
{{item-list saveItem=(action “saveItem”)}} {{item-detail saveItem=(action saveItem)}} <button onclick={{action saveItem item}}>
items components/item-list components/item-detail ACTION PASSING
CLOSURE ACTIONS ACTION PASSING CURRYING RETURN VALUES FIRST ARGUMENT VALUE
OVERRIDING FUNCTIONS
{{item-list itemAction=(action “doAction”)}} {{item-detail itemAction=(action itemAction item)}} <button onclick={{action itemAction
“save”}}> <button onclick={{action itemAction “destroy”}}> items components/item-list components/item-detail CURRYING
{{item-list itemAction=(action “doAction”)}} {{item-detail itemAction=(action itemAction item)}} <button onclick={{action itemAction
“save”}}> <button onclick={{action itemAction “destroy”}}> items components/item-list components/item-detail CURRYING
// controllers/items.js actions: { doAction(item, action) { item[action](); } }
ITEMS {{! templates/items.hbs {{item-list itemAction=(action “doAction”)}}
// controllers/items.js actions: { doAction(item, action) { item[action](); } }
ITEMS {{! templates/items.hbs {{item-list itemAction=(action “doAction”)}}
// controllers/items.js actions: { doAction(item, action) { item[action](); } }
ITEMS {{! templates/items.hbs {{item-list itemAction=(action “doAction”)}}
// controllers/items.js actions: { doAction(item, action) { item[action](); } }
ITEMS {{! templates/items.hbs {{item-list itemAction=(action “doAction”)}}
{{item-list itemAction=(action “doAction”)}} {{item-detail itemAction=(action itemAction item)}} <button onclick={{action itemAction
“save”}}> <button onclick={{action itemAction “destroy”}}> items components/item-list components/item-detail CURRYING
{{! templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item itemAction=(action itemAction
item)}} {{/each}} COMPONENTS/ITEM-LIST
{{! templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item itemAction=(action itemAction
item)}} {{/each}} COMPONENTS/ITEM-LIST
{{item-list itemAction=(action “doAction”)}} {{item-detail itemAction=(action itemAction item)}} <button onclick={{action itemAction
“save”}}> <button onclick={{action itemAction “destroy”}}> items components/item-list components/item-detail CURRYING
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “save”}}> Save </button> <button onclick={{action
itemAction “destroy”}}> Destroy </button> COMPONENTS/ITEM-DETAIL
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “save”}}> Save </button> <button onclick={{action
itemAction “destroy”}}> Destroy </button> COMPONENTS/ITEM-DETAIL
{{item-list itemAction=(action “doAction”)}} {{item-detail itemAction=(action itemAction item)}} <button onclick={{action itemAction
“save”}}> <button onclick={{action itemAction “destroy”}}> items components/item-list components/item-detail CURRYING
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “save”}}> Save </button> CURRYING {{!
templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item itemAction=(action itemAction item)}} {{/each}}
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “save”}}> Save </button> CURRYING {{!
templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item itemAction=(action itemAction item)}} {{/each}} {{action itemAction item “save”}}
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “destroy”}}> Destroy </button> CURRYING {{!
templates/components/item-list.hbs {{#each items as |item|}} {{item-detail item=item itemAction=(action itemAction item)}} {{/each}} {{action itemAction item “destroy”}}
{{item-list itemAction=(action “doAction”)}} {{item-detail itemAction=(action itemAction item)}} <button onclick={{action itemAction
“save”}}> <button onclick={{action itemAction “destroy”}}> items components/item-list components/item-detail CURRYING
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “save”}}> Save </button> CURRYING //
controllers/items.js actions: { doAction(item, action) { item[action](); } } {{action itemAction item “save”}}
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “save”}}> Save </button> CURRYING //
controllers/items.js actions: { doAction(item, action) { item[action](); // item.save(); } } {{action itemAction item “save”}}
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “destroy”}}> Save </button> CURRYING //
controllers/items.js actions: { doAction(item, action) { item[action](); } } {{action itemAction item “destroy”}}
{{! templates/components/item-detail.hbs <button onclick={{action itemAction “destroy”}}> Save </button> CURRYING //
controllers/items.js actions: { doAction(item, action) { item[action](); // item.destroy(); } } {{action itemAction item “destroy”}}
CLOSURE ACTIONS ACTION PASSING CURRYING RETURN VALUES FIRST ARGUMENT VALUE
OVERRIDING FUNCTIONS
RETURN VALUES {{login-form login=“login”}} login components/login-form user <button {{action=“submit”}}>
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password, setUser) { let user
= this._login(username, password); setUser(user); } } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password) { return this._login(username, password);
} } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password) { return this._login(username, password);
} } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password) { return this._login(username, password);
} } {{! templates/login.hbs }} {{login-form login=“login”}} LOGIN
// controllers/login.js actions: { login(username, password) { return this._login(username, password);
} } {{! templates/login.hbs }} {{login-form login=(action “login”)}} LOGIN
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button {{action “submit”}}>Login</button> {{/if}} COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button {{action “submit”}}>Login</button> {{/if}} COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button onclick={{action “submit”}}> Login </button> {{/if}} COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let setUser = user
=> set(this, ‘user’, user); this.sendAction(‘login’, get(this, ‘username’), get(this, ‘password’), setUser); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let user = get(this,
‘login’)( get(this, ‘username’), get(this, ‘password’) ); set(this, ‘user’, user); } } COMPONENTS/LOGIN-FORM
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button onclick={{action “submit”}}> Login </button> {{/if}} COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let user = get(this,
‘login’)( get(this, ‘username’), get(this, ‘password’) ); set(this, ‘user’, user); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let user = get(this,
‘login’)( get(this, ‘username’), get(this, ‘password’) ); set(this, ‘user’, user); } } COMPONENTS/LOGIN-FORM
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
// controllers/login.js actions: { login(username, password) { return this._login(username, password);
} } {{! templates/login.hbs }} {{login-form login=(action “login”)}} LOGIN
// controllers/login.js actions: { login(username, password) { return this._login(username, password);
} } {{! templates/login.hbs }} {{login-form login=(action “login”)}} LOGIN
// controllers/login.js actions: { login(username, password) { return this._login(username, password);
} } {{! templates/login.hbs }} {{login-form login=(action “login”)}} LOGIN
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
{{login-form login=(action “login”)}} login components/login-form RETURN VALUES user <button onclick={{action
“submit”}}>
// components/login-form.js actions: { submit() { let user = get(this,
‘login’)( get(this, ‘username’), get(this, ‘password’) ); set(this, ‘user’, user); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let user = get(this,
‘login’)( get(this, ‘username’), get(this, ‘password’) ); set(this, ‘user’, user); } } COMPONENTS/LOGIN-FORM
// components/login-form.js actions: { submit() { let user = get(this,
‘login’)( get(this, ‘username’), get(this, ‘password’) ); set(this, ‘user’, user); } } COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button onclick={{action “submit”}}> Login </button> {{/if}} COMPONENTS/LOGIN-FORM
{{! templates/components/login-form.hbs }} {{#if user}} You have been successfully logged
in as: {{user.username}} {{else}} {{input value=username}} {{input value=password}} <button onclick={{action “submit”}}> Login </button> {{/if}} COMPONENTS/LOGIN-FORM
RETURN VALUES {{login-form login=“login”}} login components/login-form user <button {{action=“submit”}}>
CLOSURE ACTIONS ACTION PASSING CURRYING RETURN VALUES FIRST ARGUMENT VALUE
OVERRIDING FUNCTIONS
<input oninput={{action “updateValue”}} /> FIRST ARGUMENT VALUE
<input oninput={{action “updateValue”}} /> actions: { updateValue() { }
} FIRST ARGUMENT VALUE
<input oninput={{action “updateValue”}} /> actions: { updateValue() { }
} FIRST ARGUMENT VALUE
<input oninput={{action “updateValue”}} /> actions: { updateValue() { }
} FIRST ARGUMENT VALUE event attribute on element
<input oninput={{action “updateValue”}} /> actions: { updateValue(event) { }
} FIRST ARGUMENT VALUE
<input oninput={{action “updateValue”}} /> actions: { updateValue(event) { set(this, ‘value’,
event.target.value); } } FIRST ARGUMENT VALUE
CAN WE MAKE THIS NICER?
<input oninput={{action “updateValue”}} /> actions: { updateValue(event) { set(this, ‘value’,
event.target.value); } } FIRST ARGUMENT VALUE
<input oninput={{action “updateValue” value=“target.value”}} /> actions: { updateValue(event) {
set(this, ‘value’, event.target.value); } } FIRST ARGUMENT VALUE
<input oninput={{action “updateValue” value=“target.value”}} /> actions: { updateValue(event) {
set(this, ‘value’, event.target.value); } } FIRST ARGUMENT VALUE
<input oninput={{action “updateValue” value=“target.value”}} /> actions: { updateValue(event) {
set(this, ‘value’, event.target.value); } } FIRST ARGUMENT VALUE
<input oninput={{action “updateValue” value=“target.value”}} /> actions: { updateValue(event) {
set(this, ‘value’, event.target.value); } } FIRST ARGUMENT VALUE gets value from given path on first argument
<input oninput={{action “updateValue” value=“target.value”}} /> actions: { updateValue(event) {
set(this, ‘value’, event.target.value); } } FIRST ARGUMENT VALUE
<input oninput={{action “updateValue” value=“target.value”}} /> actions: { updateValue(value) {
set(this, ‘value’, value); } } FIRST ARGUMENT VALUE
<input oninput={{action “updateValue” value=“target.value”}} /> FIRST ARGUMENT VALUE
<input oninput={{action “updateValue” value=“target.value”}} /> FIRST ARGUMENT VALUE
<input oninput={{action updateValue value=“target.value”}} /> FIRST ARGUMENT VALUE
{{! templates/components/my-input.hbs }} <input oninput={{action updateValue value=“target.value”}} /> FIRST
ARGUMENT VALUE
{{! templates/components/my-input.hbs }} <input value={{value}} oninput={{action updateValue value=“target.value”}} />
FIRST ARGUMENT VALUE
FIRST ARGUMENT VALUE {{my-input value=value updateValue=(action “updateValue”) }}
FIRST ARGUMENT VALUE {{my-input value=value updateValue=(action “updateValue”) }} actions:
{ updateValue(value) { set(this, ‘value’, value); } }
CLOSURE ACTIONS ACTION PASSING CURRYING RETURN VALUES FIRST ARGUMENT VALUE
OVERRIDING FUNCTIONS
// components/my-input.js input(value) { get(this, ‘update’)(value); } OVERRIDING FUNCTIONS
// components/my-input.js input(value) { get(this, ‘update’)(value); } OVERRIDING FUNCTIONS <script>eval(‘alert(“1337
HAXX”);’)</script>
// components/my-input.js input(value) { get(this, ‘update’)(value); } sanitize(input) { return
input; } OVERRIDING FUNCTIONS
// components/my-input.js input(value) { let sanitized = this.sanitize(value); get(this, ‘update’)(sanitized);
} sanitize(input) { return input; } OVERRIDING FUNCTIONS
// components/my-input.js input(value) { let sanitized = this.sanitize(value); get(this, ‘update’)(sanitized);
} sanitize(input) { return input; } OVERRIDING FUNCTIONS
// components/my-sanitizing-input.js export default MyInput.extend({ sanitize(input) { return escapeEvilHTML(input); }
}); OVERRIDING FUNCTIONS
// components/my-sanitizing-input.js export default MyInput.extend({ sanitize(input) { return escapeEvilHTML(input); }
}); OVERRIDING FUNCTIONS
// components/my-sanitizing-input.js export default MyInput.extend({ sanitize(input) { return escapeEvilHTML(input); }
}); OVERRIDING FUNCTIONS
// components/my-sanitizing-input.js export default MyInput.extend({ sanitize(input) { return escapeEvilHTML(input); }
}); OVERRIDING FUNCTIONS
// components/my-input.js input(value) { let sanitized = this.sanitize(value); get(this, ‘update’)(sanitized);
} sanitize(input) { return input; } OVERRIDING FUNCTIONS
// components/my-input.js input(value) { let sanitized = this.sanitize(value); get(this, ‘update’)(sanitized);
} sanitize(input) { return input; } OVERRIDING FUNCTIONS
// components/my-input.js input(value) { let sanitized = this.sanitize(value); get(this, ‘update’)(sanitized);
} sanitize(input) { return input; } OVERRIDING FUNCTIONS an attribute
{{my-input update=(action “update”) sanitize=(action “myCustomSanitize”) }} OVERRIDING FUNCTIONS
{{my-input update=(action “update”) sanitize=(action “myCustomSanitize”) }} OVERRIDING FUNCTIONS
TROLLING OPPORTUNITY
// What will happen? actions: { alert() { alert(‘1337 HAXX0R’);
} } {{my-component init=(action “alert”)}} OVERRIDING FUNCTIONS
// This will happen! Assertion Failed: You must call
`this._super` when implementing `init` in a component. OVERRIDING FUNCTIONS
OVERRIDING FUNCTIONS
CLOSURE ACTIONS AWESOME ADDONS THAT IMPROVE ON
EMBER-INVOKE-ACTION
EMBER-INVOKE-ACTION Abstracts away some ceremony around calling closure actions Backwards
compatible with classic actions
sendAction send EMBER-INVOKE-ACTION
sendAction send EMBER-INVOKE-ACTION
sendAction send EMBER-INVOKE-ACTION invokeAction invoke
EMBER-ROUTE-ACTION-HELPER
EMBER-ROUTE-ACTION-HELPER Call actions on your route as closure actions Another
reason to forget about controllers
saveModel=(route-action “save”) EMBER-ROUTE-ACTION-HELPER
EMBER-COMPOSABLE-HELPERS
<button {{action (pipe addToCart purchase goToThankYouPage)
item}} > 1-Click Buy </button> EMBER-COMPOSABLE-ACTIONS
<button {{action (pipe addToCart purchase goToThankYouPage)
item}} > 1-Click Buy </button> EMBER-COMPOSABLE-ACTIONS
<button {{action (pipe addToCart purchase goToThankYouPage)
item}} > 1-Click Buy </button> EMBER-COMPOSABLE-ACTIONS
<button {{action (pipe addToCart purchase goToThankYouPage)
item}} > 1-Click Buy </button> EMBER-COMPOSABLE-ACTIONS
<button {{action (pipe addToCart purchase goToThankYouPage)
item}} > 1-Click Buy </button> EMBER-COMPOSABLE-ACTIONS
The square of 4 is {{compute (action "square") 4}}
EMBER-COMPOSABLE-ACTIONS
None