3.8 Templates -- Actions
一、The {{action}} Helper
你的应用程序通常会需要一种方法来让用户用控件交互改变应用程序状态。
例如,你有一个显示blog post的模板,并支持用额外的信息扩展post。
可以使用{{action}}去让一个HTML元素可点击。当用户点击这个元素,命名的事件将会被发送到你的应用程序。
app/templates/post.hbs
<div class='intro'> {{intro}} </div> {{#if isExpanded}} <div class='body'>{{body}}</div> <button {{action 'contract'}}>Contract</button> {{else}} <button {{action 'expand'}}>Show More...</button> {{/if}}
app/controllers/post.js
export default Ember.Controller.extend({ intro: Ember.computed.alias('model.intro'), body: Ember.computed.alias('model.body'), // initial value isExpanded: false, actions: { expand() { this.set('isExpanded', true); }, contract() { this.set('isExpanded', false); } } });
- 注意actions可以附加到任何DOM元素,但并不是所有的都响应单击事件。例如如果一个action被附加到了一个没有href属性的a标签上,或者一个div,一些浏览器将不会执行相关的function。
- 如果真的需要为这些元素定义这些actions,一个CSS可以使它们可点击,cursor: pointer。
-
[data-ember-action] { cursor: pointer; }
二、Action Bubbling(Action 冒泡)
1.默认,{{action}}触发模板的controller上的方法,正如上文所述。
如果在actions对象中controller没有实现和它同名的方法,这个action会被发送到路由器,当前活动的子路由将会有机会处理这个action。
处理actions的routes和controllers必须将action handlers放置在一个actions hash中。即使一个路由有一个与actions同名的方法,除非它是在actions的哈希表中,否则不会被触发。在controllers的例子中,虽然在controller上有不支持直接触发的方法,还是强烈建议把你的actions处理方法放入一个actions哈希表中,以向前兼容。
app/routes/post.js
export default Ember.Route.extend({ actions: { expand() { this.controller.set('isExpanded', true); }, contract() { this.controller.set('isExpanded', false); } } });
你可以看到,action的处理程序被调用时执行,this是route,而不是actions hash。
2. 为了让action继续冒泡,你必须从处理程序中返回true。
app/routes/post.js
export default Ember.Route.extend({ actions: { expand() { this.controller.set('isExpanded', true); }, contract() { // ... if (actionShouldAlsoBeTriggeredOnParentRoute) { return true; } } } });
3. 如果模板的控制器和当前活动路由没有实现一个处理程序,该action将继续查找任何父路由。最终,如果一个ApplicationRoute被定义了,它将会有机会处理这个action。
4. 当一个action被触发,但是在controller,当前route或者任何当前route的祖先中没有找到匹配的action处理程序,将会抛出错误。
5. 查找action handler的过程如下图所示:
6. 这允许你创建一个按钮,但是基于应用程序的不同位置具有不同的行为。例如,当是/posts路由的时候你可能想在侧边栏有一个按钮做一件事,当是/about路由的时候做另外一件事。
三、Action Parameters
你可以有选择的给aciton处理程序传递参数。任何值都是在action name作为参数被传递给handler之后被传递给{{action}} helper的。
例如,如果post参数被传递:
<p><button {{action "select" post}}>✓</button> {{post.title}}</p>
controller的select action处理程序会被调用,使用一个包含post model的单个参数:
app/controllers/post.js
export default Ember.Controller.extend({ actions: { select(post) { console.log(post.get('title')); } } });
四、Specifying the type of event
默认的,当用户点击元素时{{action}} helper监听click事件并触发action。
你可以通on选项指定可选择的事件:
<p> <button {{action "select" post on="mouse-up"}}>✓</button> {{post.title}} </p>
你应该使用dasherized事件名。一般,两个单词的事件名(像keypress)写成key-press。
五、Specifying Whitelisted modifier keys(白名单)
默认的,当用户click这个元素时{{action}}将会忽略键盘上按键的click事件。你可以提供一个allowedKeys选项指定那些keys不应该被忽略。
<button {{action 'anActionName' allowedKeys="alt"}}> click me </button>
这样,{{action}}在键盘上的"alt"键按下时将被触发。
六、Default Action
默认的,通过{{action}}在所有处理的事件中event.preventDefault()会被调用。避免这样可以添加参数preventDefault=false。
七、Stopping Event Propagation(停止传播事件)
默认的,{{action}}允许事件处理冒泡到父节点。如果你想停止事件冒泡,你可以在父节点中 disable propagation。
例如,如果在一个链接中有一个X按钮,你希望当用户点击X时保证link没有被点击:
{{#link-to 'post'}} Post <button {{action 'close' bubbles=false}}>✗</button> {{/link-to}}
- 没有bubbles=false的话,如果用点击按钮,Ember会触发这个action,并且将会把click传播到link。
- 如果有bubbles=false,Ember将会阻止浏览器传播该事件。
八、Handing an Action
{{action}}发送action从一个组件模板到另外一个组件。
你可以通过在组件中添加一个包含和action同名方法的actions hash来处理一个action。
例如,假设这个模板为按钮添加select action。
app/templates/component/show-posts.hbs
<button {{action "select" model}}>Select Post</button>
你可以实现一个function通过添加包含一个名为select的方法的actions hash来响应按钮的点击。
app/components/show-post.js
export default Ember.Component.extend({ actions: { select(post) { // do your business. } } });
九、Allowing Default Browser Action
默认的,{{action}}阻止DOM事件默认的浏览器行为。如果你想允许浏览器行为,你可以通过阻止它来停止ember。
例如,如果你有一个普通的link并希望点击的时候该链接带用户去另一个页面而不是触发一个ember action:
<a href="newPage.htm" {{action 'logClick' preventDefault=false}}>Go</a>
- 没有preventDefault=false的话,如果用户点击该链接,Ember将会触发action,但是用户仍然在当前页面。
- 有preventDefault=false的话,如果用点击该链接,Ember将会触发action,用户会被导向新页面。