|
1 |
| -Octane provides a set of new conventional APIs for creating and adding event |
2 |
| -handlers and actions to your components and templates: |
3 |
| - |
4 |
| -* The `@action` decorator |
5 |
| -* The `{{on}}` modifier |
6 |
| -* The `{{fn}}` helper |
7 |
| - |
8 |
| -These are meant to replace the `{{action}}` helper/modifier, which will be |
9 |
| -deprecated in the future. You can use them like this: |
10 |
| - |
11 |
| -```javascript |
12 |
| -import Component from '@glimmer/component'; |
13 |
| -import { action } from '@ember/object'; |
14 |
| - |
15 |
| -export default class TodoComponent extends Component { |
16 |
| - @action |
17 |
| - toggleCompleted(isComplete) { |
18 |
| - // ... |
19 |
| - } |
20 |
| -} |
21 |
| -``` |
22 |
| - |
23 |
| -```handlebars |
24 |
| -<button type="button" {{on "click" (fn this.toggleCompleted true)}}>Complete</button> |
25 |
| -``` |
26 |
| - |
27 |
| -## Benefits of `@action`, `{{on}}`, and `{{fn}}` |
28 |
| - |
29 |
| -`{{action}}` has a number of functions, including: |
30 |
| - |
31 |
| -* Creating action callbacks, which bind the _context_ of the callback (the |
32 |
| - component/controller). |
33 |
| -* Adding arguments to action callbacks: |
34 |
| - |
35 |
| - ```handlebars |
36 |
| - <!-- passes 123 to the 'setValue' action --> |
37 |
| - <MyComponent @onClick={{action 'setValue' 123}} /> |
38 |
| - ``` |
39 |
| - |
40 |
| -* Adding event handlers to elements (when used as a modifier): |
41 |
| - |
42 |
| - ```handlebars |
43 |
| - <button type="button" {{action 'sayHello'}}>Say Hello!</button> |
44 |
| - ``` |
45 |
| - |
46 |
| -The new APIs split each of these pieces of functionality out into one clearly |
47 |
| -defined API: |
48 |
| - |
49 |
| -* `@action` is a decorator that binds a method to the context its used in |
50 |
| -* `{{on}}` is a modifier that's used to add event listeners to DOM elements |
51 |
| -* `{{fn}}` is a helper that adds arguments to another function or callback |
52 |
| - |
53 |
| -This keeps the responsibilities clearly delineated, and makes it much easier to |
54 |
| -reason about what each individual API is doing. |
55 |
| - |
56 |
| -## Getting used to `@action`, `{{on}}`, and `{{fn}}` |
57 |
| - |
58 |
| -### The `@action` Decorator |
59 |
| - |
60 |
| -In Ember Octane, actions are no longer defined on the `actions` object of a |
61 |
| -component or controller. Instead, they are standard class methods decorated with |
62 |
| -the `@action` decorator. |
63 |
| - |
64 |
| -Before: |
65 |
| - |
66 |
| -```javascript |
67 |
| -import Component from '@ember/component'; |
68 |
| - |
69 |
| -export default Component.extend({ |
70 |
| - actions: { |
71 |
| - doSomething() { |
72 |
| - // ... |
73 |
| - } |
74 |
| - } |
75 |
| -}) |
76 |
| -``` |
77 |
| - |
78 |
| -After: |
79 |
| - |
80 |
| -```javascript |
81 |
| -import Component from '@glimmer/component'; |
82 |
| - |
83 |
| -export default class ExampleComponent extends Component { |
84 |
| - @action |
85 |
| - doSomething() { |
86 |
| - // ... |
87 |
| - } |
88 |
| -} |
89 |
| -``` |
90 |
| - |
91 |
| -The decorator leaves the method intact without any changes, so you can continue |
92 |
| -to use it like a normal method. This also means that you can reference the |
93 |
| -action directly in templates, instead of using strings. |
94 |
| - |
95 |
| -Before: |
96 |
| - |
97 |
| -```handlebars |
98 |
| -<button type="button" {{action "doSomething"}}>Click Me!</button> |
99 |
| -``` |
100 |
| - |
101 |
| -After: |
102 |
| - |
103 |
| -```handlebars |
104 |
| -<button type="button" {{on "click" this.doSomething}}>Click Me!</button> |
105 |
| -``` |
106 |
| - |
107 |
| -The decorator _is_ important, as it binds the action directly to the class so it |
108 |
| -can reference it later on. |
109 |
| - |
110 |
| -### The `{{on}}` Modifier |
111 |
| - |
112 |
| -The API for `{{on}}` is the same as JavaScript's native [`addEventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener). It receives the event name as the first argument, and a |
113 |
| -callback function as the second argument: |
114 |
| - |
115 |
| -```handlebars |
116 |
| -<button type="button" {{on "click" this.handleClick}}>Click Me!</button> |
117 |
| -``` |
118 |
| - |
119 |
| -The event can be _any_ event name, not just the `click` event, which makes |
120 |
| -`{{on}}` perfect for handling any kind of DOM event. For a list of native |
121 |
| -browser events, see the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/Events). |
122 |
| -The callback function will receive the event as its first argument: |
123 |
| - |
124 |
| -```javascript |
125 |
| -import Component from '@glimmer/component'; |
126 |
| -import { action } from '@ember/object'; |
127 |
| - |
128 |
| -export default class ExampleComponent extends Component { |
129 |
| - @action |
130 |
| - handleClick(event) { |
131 |
| - event.preventDefault(); |
132 |
| - } |
133 |
| -} |
134 |
| -``` |
135 |
| - |
136 |
| -<div class="cta"> |
137 |
| - <div class="cta-note"> |
138 |
| - <div class="cta-note-body"> |
139 |
| - <div class="cta-note-heading">Zoey says...</div> |
140 |
| - <div class="cta-note-message"> |
141 |
| - The <code>{{action}}</code> modifier called <code>event.preventDefault()</code> under the hood, but the <code>{{on}}</code> modifier does not, so if you need to do anything other than the default action for a particular event, you must call <code>event.preventDefault</code> within the action. |
142 |
| - </div> |
143 |
| - </div> |
144 |
| - <img src="/images/mascots/zoey.png" role="presentation" alt=""> |
145 |
| - </div> |
146 |
| -</div> |
147 |
| - |
148 |
| -This is a replacement for `{{action}}` when it is used as a modifier: |
149 |
| - |
150 |
| -```handlebars |
151 |
| -<!-- Before --> |
152 |
| -<button type="button" {{action 'handleClick'}}>Click Me!</button> |
153 |
| -<button type="button" {{action 'handleDoubleClick' on="doubleClick"}}>Double Click Me!</button> |
154 |
| -
|
155 |
| -<!-- After --> |
156 |
| -<button type="button" {{on "click" this.handleClick}}>Click Me!</button> |
157 |
| -<button type="button" {{on "dblclick" this.handleDoubleClick}}>Double Click Me!</button> |
158 |
| -``` |
159 |
| - |
160 |
| -You can also pass additional options such as `passive` and `once` as named |
161 |
| -parameters to the modifier: |
162 |
| - |
163 |
| -```handlebars |
164 |
| -<button type="button" {{on "click" this.handleClick passive=true}}>Click Me!</button> |
165 |
| -``` |
166 |
| - |
167 |
| -If you ever used the `value` parameter of `{{action}}`, there is no direct |
168 |
| -equivalent for `{{on}}`. You should instead write an action that gets the value |
169 |
| -for you. |
170 |
| - |
171 |
| -Before: |
172 |
| - |
173 |
| -```handlebars |
174 |
| -<input value={{this.value}} onchange={{action (mut this.value) value="target.value"}} /> |
175 |
| -``` |
176 |
| - |
177 |
| -After: |
178 |
| - |
179 |
| -```handlebars |
180 |
| -<input value={{this.value}} {{on "change" this.updateValue}} /> |
181 |
| -``` |
182 |
| -```javascript |
183 |
| -import Component from '@glimmer/component'; |
184 |
| -import { tracked } from '@glimmer/tracking'; |
185 |
| -import { action } from '@ember/object'; |
186 |
| - |
187 |
| -export default class ExampleComponent extends Component { |
188 |
| - @tracked value; |
189 |
| - |
190 |
| - @action |
191 |
| - updateValue(event) { |
192 |
| - this.value = event.target.value; |
193 |
| - } |
194 |
| -} |
195 |
| -``` |
196 |
| - |
197 |
| -If you want to pass additional parameters to the callback function, you must use |
198 |
| -the `{{fn}}` helper. `{{on}}` does not receive any additional parameters. |
199 |
| - |
200 |
| -### The `{{fn}}` Helper |
201 |
| - |
202 |
| -`{{fn}}` is a helper that receives a function and some arguments, and returns |
203 |
| -a new function that combines. This allows you to pass parameters along to |
204 |
| -functions in your templates: |
205 |
| - |
206 |
| -```handlebars |
207 |
| -<button type="button" {{on "click" (fn this.handleClick 123)}}>Click Me!</button> |
208 |
| -``` |
209 |
| - |
210 |
| -```javascript |
211 |
| -import Component from '@glimmer/component'; |
212 |
| -import { action } from '@ember/object'; |
213 |
| - |
214 |
| -export default class ExampleComponent extends Component { |
215 |
| - @action |
216 |
| - handleClick(value) { |
217 |
| - console.log(value); // 123 |
218 |
| - } |
219 |
| -} |
220 |
| -``` |
221 |
| - |
222 |
| -This is a replacement for passing parameters to the `{{action}}` modifier or |
223 |
| -helper: |
224 |
| - |
225 |
| -```handlebars |
226 |
| -<!-- Before --> |
227 |
| -<button type="button" {{action 'handleClick' 123}}>Click Me!</button> |
228 |
| -<MyComponent @onClick={{action 'handleClick' 123}} /> |
229 |
| -
|
230 |
| -<!-- After --> |
231 |
| -<button type="button" {{on "click" (fn this.handleClick 123)}}>Click Me!</button> |
232 |
| -<MyComponent @onClick={{fn this.handleClick 123}} /> |
233 |
| -``` |
234 |
| - |
235 |
| -<!-- eof - needed for pages that end in a code block --> |
| 1 | +--- |
| 2 | +redirect: upgrading/current-edition |
| 3 | +--- |
0 commit comments