|
| 1 | +# User input |
| 2 | + |
| 3 | +User actions such as clicking a link, pushing a button, and entering |
| 4 | +text raise DOM events. |
| 5 | +This page explains how to bind those events to component event handlers using the Angular |
| 6 | +event binding syntax. |
| 7 | + |
| 8 | +Run the <live-example></live-example>. |
| 9 | + |
| 10 | + |
| 11 | +## Binding to user input events |
| 12 | + |
| 13 | +You can use [Angular event bindings](guide/event-binding) |
| 14 | +to respond to any [DOM event](https://developer.mozilla.org/en-US/docs/Web/Events). |
| 15 | +Many DOM events are triggered by user input. Binding to these events provides a way to |
| 16 | +get input from the user. |
| 17 | + |
| 18 | +To bind to a DOM event, surround the DOM event name in parentheses and assign a quoted |
| 19 | +[template statement](guide/template-statements) to it. |
| 20 | + |
| 21 | +The following example shows an event binding that implements a click handler: |
| 22 | + |
| 23 | +<code-example path="user-input/src/app/click-me.component.ts" region="click-me-button" header="src/app/click-me.component.ts"></code-example> |
| 24 | + |
| 25 | +{@a click} |
| 26 | + |
| 27 | +The `(click)` to the left of the equals sign identifies the button's click event as the **target of the binding**. |
| 28 | +The text in quotes to the right of the equals sign |
| 29 | +is the **template statement**, which responds |
| 30 | +to the click event by calling the component's `onClickMe` method. |
| 31 | + |
| 32 | +When writing a binding, be aware of a template statement's **execution context**. |
| 33 | +The identifiers in a template statement belong to a specific context object, |
| 34 | +usually the Angular component controlling the template. |
| 35 | +The example above shows a single line of HTML, but that HTML belongs to a larger component: |
| 36 | + |
| 37 | + |
| 38 | +<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" header="src/app/click-me.component.ts"></code-example> |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +When the user clicks the button, Angular calls the `onClickMe` method from `ClickMeComponent`. |
| 43 | + |
| 44 | + |
| 45 | + |
| 46 | +## Get user input from the $event object |
| 47 | +DOM events carry a payload of information that may be useful to the component. |
| 48 | +This section shows how to bind to the `keyup` event of an input box to get the user's input after each keystroke. |
| 49 | + |
| 50 | +The following code listens to the `keyup` event and passes the entire event payload (`$event`) to the component event handler. |
| 51 | + |
| 52 | +<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-template" header="src/app/keyup.components.ts (template v.1)"></code-example> |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +When a user presses and releases a key, the `keyup` event occurs, and Angular provides a corresponding |
| 57 | +DOM event object in the `$event` variable which this code passes as a parameter to the component's `onKey()` method. |
| 58 | + |
| 59 | +<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class-no-type" header="src/app/keyup.components.ts (class v.1)"></code-example> |
| 60 | + |
| 61 | + |
| 62 | + |
| 63 | +The properties of an `$event` object vary depending on the type of DOM event. For example, |
| 64 | +a mouse event includes different information than an input box editing event. |
| 65 | + |
| 66 | +All [standard DOM event objects](https://developer.mozilla.org/en-US/docs/Web/API/Event) |
| 67 | +have a `target` property, a reference to the element that raised the event. |
| 68 | +In this case, `target` refers to the [`<input>` element](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement) and |
| 69 | +`event.target.value` returns the current contents of that element. |
| 70 | + |
| 71 | +After each call, the `onKey()` method appends the contents of the input box value to the list |
| 72 | +in the component's `values` property, followed by a separator character (|). |
| 73 | +The [interpolation](guide/interpolation) |
| 74 | +displays the accumulating input box changes from the `values` property. |
| 75 | + |
| 76 | +Suppose the user enters the letters "abc", and then backspaces to remove them one by one. |
| 77 | +Here's what the UI displays: |
| 78 | + |
| 79 | +<code-example> |
| 80 | + a | ab | abc | ab | a | | |
| 81 | +</code-example> |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | +<div class="lightbox"> |
| 86 | + <img src='generated/images/guide/user-input/keyup1-anim.gif' alt="key up 1"> |
| 87 | +</div> |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | +<div class="alert is-helpful"> |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | +Alternatively, you could accumulate the individual keys themselves by substituting `event.key` |
| 96 | +for `event.target.value` in which case the same user input would produce: |
| 97 | + |
| 98 | +<code-example> |
| 99 | + a | b | c | backspace | backspace | backspace | |
| 100 | + |
| 101 | +</code-example> |
| 102 | + |
| 103 | + |
| 104 | + |
| 105 | +</div> |
| 106 | + |
| 107 | + |
| 108 | + |
| 109 | +{@a keyup1} |
| 110 | + |
| 111 | + |
| 112 | +### Type the _$event_ |
| 113 | + |
| 114 | +The example above casts the `$event` as an `any` type. |
| 115 | +That simplifies the code at a cost. |
| 116 | +There is no type information |
| 117 | +that could reveal properties of the event object and prevent silly mistakes. |
| 118 | + |
| 119 | +The following example rewrites the method with types: |
| 120 | + |
| 121 | +<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-1-class" header="src/app/keyup.components.ts (class v.1 - typed )"></code-example> |
| 122 | + |
| 123 | + |
| 124 | + |
| 125 | +The `$event` is now a specific `KeyboardEvent`. |
| 126 | +Not all elements have a `value` property so it casts `target` to an input element. |
| 127 | +The `OnKey` method more clearly expresses what it expects from the template and how it interprets the event. |
| 128 | + |
| 129 | +### Passing _$event_ is a dubious practice |
| 130 | +Typing the event object reveals a significant objection to passing the entire DOM event into the method: |
| 131 | +the component has too much awareness of the template details. |
| 132 | +It can't extract information without knowing more than it should about the HTML implementation. |
| 133 | +That breaks the separation of concerns between the template (_what the user sees_) |
| 134 | +and the component (_how the application processes user data_). |
| 135 | + |
| 136 | +The next section shows how to use template reference variables to address this problem. |
| 137 | + |
| 138 | + |
| 139 | + |
| 140 | +## Get user input from a template reference variable |
| 141 | +There's another way to get the user data: use Angular |
| 142 | +[**template reference variables**](guide/template-reference-variables). |
| 143 | +These variables provide direct access to an element from within the template. |
| 144 | +To declare a template reference variable, precede an identifier with a hash (or pound) character (#). |
| 145 | + |
| 146 | +The following example uses a template reference variable |
| 147 | +to implement a keystroke loopback in a simple template. |
| 148 | + |
| 149 | +<code-example path="user-input/src/app/loop-back.component.ts" region="loop-back-component" header="src/app/loop-back.component.ts"></code-example> |
| 150 | + |
| 151 | + |
| 152 | + |
| 153 | +The template reference variable named `box`, declared on the `<input>` element, |
| 154 | +refers to the `<input>` element itself. |
| 155 | +The code uses the `box` variable to get the input element's `value` and display it |
| 156 | +with interpolation between `<p>` tags. |
| 157 | + |
| 158 | +The template is completely self contained. It doesn't bind to the component, |
| 159 | +and the component does nothing. |
| 160 | + |
| 161 | +Type something in the input box, and watch the display update with each keystroke. |
| 162 | + |
| 163 | + |
| 164 | +<div class="lightbox"> |
| 165 | + <img src='generated/images/guide/user-input/keyup-loop-back-anim.gif' alt="loop back"> |
| 166 | +</div> |
| 167 | + |
| 168 | + |
| 169 | + |
| 170 | +<div class="alert is-helpful"> |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | +**This won't work at all unless you bind to an event**. |
| 175 | + |
| 176 | +Angular updates the bindings (and therefore the screen) |
| 177 | +only if the app does something in response to asynchronous events, such as keystrokes. |
| 178 | +This example code binds the `keyup` event |
| 179 | +to the number 0, the shortest template statement possible. |
| 180 | +While the statement does nothing useful, |
| 181 | +it satisfies Angular's requirement so that Angular will update the screen. |
| 182 | + |
| 183 | +</div> |
| 184 | + |
| 185 | + |
| 186 | + |
| 187 | +It's easier to get to the input box with the template reference |
| 188 | +variable than to go through the `$event` object. Here's a rewrite of the previous |
| 189 | +`keyup` example that uses a template reference variable to get the user's input. |
| 190 | + |
| 191 | +<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-2" header="src/app/keyup.components.ts (v2)"></code-example> |
| 192 | + |
| 193 | + |
| 194 | + |
| 195 | +A nice aspect of this approach is that the component gets clean data values from the view. |
| 196 | +It no longer requires knowledge of the `$event` and its structure. |
| 197 | +{@a key-event} |
| 198 | + |
| 199 | + |
| 200 | +## Key event filtering (with `key.enter`) |
| 201 | +The `(keyup)` event handler hears *every keystroke*. |
| 202 | +Sometimes only the _Enter_ key matters, because it signals that the user has finished typing. |
| 203 | +One way to reduce the noise would be to examine every `$event.keyCode` and take action only when the key is _Enter_. |
| 204 | + |
| 205 | +There's an easier way: bind to Angular's `keyup.enter` pseudo-event. |
| 206 | +Then Angular calls the event handler only when the user presses _Enter_. |
| 207 | + |
| 208 | +<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-3" header="src/app/keyup.components.ts (v3)"></code-example> |
| 209 | + |
| 210 | + |
| 211 | + |
| 212 | +Here's how it works. |
| 213 | + |
| 214 | +<div class="lightbox"> |
| 215 | + <img src='generated/images/guide/user-input/keyup3-anim.gif' alt="key up 3"> |
| 216 | +</div> |
| 217 | + |
| 218 | + |
| 219 | + |
| 220 | + |
| 221 | +## On blur |
| 222 | + |
| 223 | +In the previous example, the current state of the input box |
| 224 | +is lost if the user mouses away and clicks elsewhere on the page |
| 225 | +without first pressing _Enter_. |
| 226 | +The component's `value` property is updated only when the user presses _Enter_. |
| 227 | + |
| 228 | +To fix this issue, listen to both the _Enter_ key and the _blur_ event. |
| 229 | + |
| 230 | + |
| 231 | +<code-example path="user-input/src/app/keyup.components.ts" region="key-up-component-4" header="src/app/keyup.components.ts (v4)"></code-example> |
| 232 | + |
| 233 | + |
| 234 | + |
| 235 | + |
| 236 | +## Put it all together |
| 237 | +The previous page showed how to [display data](guide/displaying-data). |
| 238 | +This page demonstrated event binding techniques. |
| 239 | + |
| 240 | +Now, put it all together in a micro-app |
| 241 | +that can display a list of heroes and add new heroes to the list. |
| 242 | +The user can add a hero by typing the hero's name in the input box and |
| 243 | +clicking **Add**. |
| 244 | + |
| 245 | + |
| 246 | +<div class="lightbox"> |
| 247 | + <img src='generated/images/guide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes"> |
| 248 | +</div> |
| 249 | + |
| 250 | + |
| 251 | + |
| 252 | +Below is the "Little Tour of Heroes" component. |
| 253 | + |
| 254 | + |
| 255 | +<code-example path="user-input/src/app/little-tour.component.ts" region="little-tour" header="src/app/little-tour.component.ts"></code-example> |
| 256 | + |
| 257 | + |
| 258 | + |
| 259 | +### Observations |
| 260 | + |
| 261 | +* **Use template variables to refer to elements** — |
| 262 | +The `newHero` template variable refers to the `<input>` element. |
| 263 | +You can reference `newHero` from any sibling or child of the `<input>` element. |
| 264 | + |
| 265 | +* **Pass values, not elements** — |
| 266 | +Instead of passing the `newHero` into the component's `addHero` method, |
| 267 | +get the input box value and pass *that* to `addHero`. |
| 268 | + |
| 269 | +* **Keep template statements simple** — |
| 270 | +The `(blur)` event is bound to two JavaScript statements. |
| 271 | +The first statement calls `addHero`. The second statement, `newHero.value=''`, |
| 272 | +clears the input box after a new hero is added to the list. |
| 273 | + |
| 274 | + |
| 275 | + |
| 276 | +## Source code |
| 277 | + |
| 278 | +Following is all the code discussed in this page. |
| 279 | + |
| 280 | +<code-tabs> |
| 281 | + |
| 282 | + <code-pane header="click-me.component.ts" path="user-input/src/app/click-me.component.ts"> |
| 283 | + |
| 284 | + </code-pane> |
| 285 | + |
| 286 | + <code-pane header="keyup.components.ts" path="user-input/src/app/keyup.components.ts"> |
| 287 | + |
| 288 | + </code-pane> |
| 289 | + |
| 290 | + <code-pane header="loop-back.component.ts" path="user-input/src/app/loop-back.component.ts"> |
| 291 | + |
| 292 | + </code-pane> |
| 293 | + |
| 294 | + <code-pane header="little-tour.component.ts" path="user-input/src/app/little-tour.component.ts"> |
| 295 | + |
| 296 | + </code-pane> |
| 297 | + |
| 298 | +</code-tabs> |
| 299 | + |
| 300 | + |
| 301 | +Angular also supports passive event listeners. For example, you can use the following steps to make the scroll event passive. |
| 302 | + |
| 303 | +1. Create a file `zone-flags.ts` under `src` directory. |
| 304 | +2. Add the following line into this file. |
| 305 | + |
| 306 | +``` |
| 307 | +(window as any)['__zone_symbol__PASSIVE_EVENTS'] = ['scroll']; |
| 308 | +``` |
| 309 | + |
| 310 | +3. In the `src/polyfills.ts` file, before importing zone.js, import the newly created `zone-flags`. |
| 311 | + |
| 312 | +``` |
| 313 | +import './zone-flags'; |
| 314 | +import 'zone.js/dist/zone'; // Included with Angular CLI. |
| 315 | +``` |
| 316 | + |
| 317 | +After those steps, if you add event listeners for the `scroll` event, the listeners will be `passive`. |
| 318 | + |
| 319 | +## Summary |
| 320 | + |
| 321 | +You have mastered the basic primitives for responding to user input and gestures. |
| 322 | + |
| 323 | +These techniques are useful for small-scale demonstrations, but they |
| 324 | +quickly become verbose and clumsy when handling large amounts of user input. |
| 325 | +Two-way data binding is a more elegant and compact way to move |
| 326 | +values between data entry fields and model properties. |
| 327 | +The next page, `Forms`, explains how to write |
| 328 | +two-way bindings with `NgModel`. |
0 commit comments