|
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