1
1
include ../_util-fns
2
2
3
3
:marked
4
- User actions such as clicking a link, pushing a button, and entering
5
- text raise DOM events.
6
- This page explains how to bind to those events using the Angular
4
+ When the user clicks a link, pushes a button, or enters text
5
+ we want to know about it. These user actions all raise DOM events.
6
+ In this chapter we learn to bind to those events using the Angular
7
7
event binding syntax.
8
8
9
- .l-sub-section
10
- :marked
11
- Run the <live-example></live-example>.
9
+ Run the <live-example></live-example>.
12
10
13
11
:marked
14
12
## Binding to user input events
15
13
16
- You can use [Angular event bindings](./template-syntax.html#event-binding)
17
- to respond to any [DOM event](https://developer.mozilla.org/en-US/docs/Web/Events).
18
- Many DOM events are triggered by user input. Binding to these events provides a way to
19
- get input from the user.
20
-
21
- To bind to a DOM event, surround the DOM event name in parentheses and assign a quoted
22
- [template statement](./template-syntax.html#template-statements) to it.
14
+ We can use [Angular event bindings](./template-syntax.html#event-binding)
15
+ to respond to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events).
23
16
24
- The following example shows an event binding that implements a click handler:
17
+ The syntax is simple. We surround the DOM event name in parentheses and assign a quoted template statement to it.
18
+ As an example, here's an event binding that implements a click handler:
25
19
+ makeExample('user-input/ts/app/click-me.component.ts' , 'click-me-button' )( format ="." , language ="html" )
26
20
27
21
<a id =" click" ></a >
28
22
:marked
29
- The `(click)` to the left of the equals sign identifies the button's click event as the **target of the binding**.
30
- The text in quotes to the right of the equals sign
31
- is the ** template statement**, which responds
32
- to the click event by calling the component's `onClickMe` method .
23
+ The `(click)` to the left of the equal sign identifies the button's click event as the **target of the binding**.
24
+ The text within quotes on the right is the **template statement** in which we
25
+ respond to the click event by calling the component's `onClickMe` method. A [ template statement](./template-syntax.html#template-statements) is a subset
26
+ of JavaScript with restrictions and a few added tricks .
33
27
34
- When writing a binding, be aware of a template statement's **execution context**.
35
- The identifiers in a template statement belong to a specific context object,
36
- usually the Angular component controlling the template.
37
- The example above shows a single line of HTML, but that HTML belongs to a larger component:
28
+ When writing a binding we must be aware of a template statement's **execution context**.
29
+ The identifiers appearing within a statement belong to a specific context object.
30
+ That object is usually the Angular component that controls the template ... which it definitely is
31
+ in this case because that snippet of HTML belongs to the following component:
38
32
39
33
+ makeExample('user-input/ts/app/click-me.component.ts' , 'click-me-component' , 'app/click-me.component.ts' )( format ="." )
40
34
:marked
41
- When the user clicks the button, Angular calls the `onClickMe` method from `ClickMeComponent` .
35
+ When the user clicks the button, Angular calls the component's `onClickMe` method.
42
36
43
37
.l-main-section
44
38
:marked
45
39
## Get user input from the $event object
46
- This section shows how to bind to the ` keyup` event of an onscreen text box
47
- to get user input, then echo the input back to the screen.
40
+ We can bind to all kinds of events. Let's bind to the keyup event of an input box and replay
41
+ what the user types back onto the screen.
48
42
49
- The following code listens to the `keyup` event and gets the user's input.
43
+ This time we'll (1) listen to an event and (2) grab the user's input.
50
44
+ makeExample('user-input/ts/app/keyup.components.ts' , 'key-up-component-1-template' , 'app/keyup.components.ts (template v.1)' )( format ="." )
51
45
:marked
52
- When a user presses and releases a key, the `keyup` event occurs, and Angular provides a corresponding
53
- DOM event object in the `$event` variable.
54
- The event object contains the user input data; that is, whatever key the user pressed.
55
- The code passes `$event` as a parameter to the component's `onKey()` method.
46
+ Angular makes an event object available in the **`$event`** variable,
47
+ which we pass to the component's `onKey()` method.
48
+ The user data we want is in that variable somewhere.
56
49
+ makeExample('user-input/ts/app/keyup.components.ts' , 'key-up-component-1-class-no-type' , 'app/keyup.components.ts (class v.1)' )( format ="." )
57
50
:marked
58
- The expression `event.target.value` in the example code
59
- represents the character the user typed.
60
- The properties of an event object vary depending on the type of DOM event. For example,
61
- a mouse event includes different information than a textbox editing event. However, like all
62
- [standard DOM event objects](https://developer.mozilla.org/en-US/docs/Web/API/Event),
63
- the event object returned by `keyup` contains a property called
64
- `target`. The `target` property gives an
51
+ The shape of the `$event` object is determined by whatever raises the event.
52
+ The `keyup` event comes from the DOM, so `$event` must be a [standard DOM event object](https://developer.mozilla.org/en-US/docs/Web/API/Event).
53
+ The `$event.target` gives us an
65
54
[`HTMLInputElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement), which
66
- has a `value` property. For a keypress event, the `value` property contains the letter, number, or other character
67
- that the user pressed on the keyboard.
55
+ has a `value` property that contains our user input data.
68
56
69
- The `onKey()` component method gets the user's keystroke
70
- from the event object, then adds it to the list of user data
71
- in the component's `values` property.
72
- Then the [interpolation](./template-syntax.html#interpolation)
73
- displays the accumulated user input from the `values` property, followed by a pipe character (|).
57
+ The `onKey()` component method is where we extract the user's input
58
+ from the event object, adding that input to the list of user data that we're accumulating in the component's `values` property.
59
+ We then use [interpolation](./template-syntax.html#interpolation)
60
+ to display the accumulating `values` property back on screen.
74
61
75
- Suppose the user enters the letters "abc", and then backspaces to remove them one by one .
62
+ Enter the letters "abc", and then backspace to remove them.
76
63
Here's what the UI displays:
77
64
code-example() .
78
65
a | ab | abc | ab | a | |
@@ -82,77 +69,78 @@ figure.image-display
82
69
<a id =" keyup1" ></a >
83
70
.l-sub-section
84
71
:marked
85
- This example casts the `$event` as an `any` type.
86
- In this way, the code is simplified, but strong typing is lost.
87
-
88
- In the following example, the method is rewritten to cast to HTML DOM objects:
72
+ We cast the `$event` as an `any` type, which means we've abandoned strong typing
73
+ to simplify our code. We generally prefer the strong typing that TypeScript affords.
74
+ We can rewrite the method, casting to HTML DOM objects like this.
89
75
+ makeExample('user-input/ts/app/keyup.components.ts' , 'key-up-component-1-class' , 'app/keyup.components.ts (class v.1 - strongly typed )' )( format ="." )
90
76
:marked
91
77
<br>Strong typing reveals a serious problem with passing a DOM event into the method:
92
- too much awareness of template details, and too little separation of concerns.
78
+ too much awareness of template details, too little separation of concerns.
93
79
94
- The next section shows how to use template reference variables to address this problem.
80
+ We'll address this problem in our next try at processing user keystrokes.
81
+ :marked
95
82
96
83
.l-main-section
97
84
:marked
98
85
## Get user input from a template reference variable
99
- As mentioned in the previous section, it is not ideal to pass an event variable into a method
100
- to get user input. But there's another way to get the user data: use Angular
101
- [**template reference variables**](./template-syntax.html#ref-vars).
102
- These variables grant direct access to an element.
103
- To declare a template reference variable, precede an identifier with a hash (or pound) character (#).
104
-
105
- The following example uses a template reference variable
106
- to implement a keystroke loopback in a simple template.
86
+ There's another way to get the user data without the `$ event` variable.
87
+
88
+ Angular has a syntax feature called [**template reference variables**](./template-syntax.html#ref-vars).
89
+ These variables grant us direct access to an element.
90
+ We declare a template reference variable by preceding an identifier with a hash/ pound character (#).
91
+
92
+ Here's an example of using a template reference variable
93
+ to implement a clever keystroke loopback in an ultra- simple template.
107
94
+ makeExample('user-input/ts/app/loop-back.component.ts' , 'loop-back-component' , 'app/loop-back.component.ts' )( format ="." )
108
95
:marked
109
- The template reference variable named `box`, declared on the `<input>` element,
110
- refers to the `<input>` element itself.
111
- The code uses the `box` variable to get the input element's `value` and display it
96
+ We've declared a template reference variable named `box` on the `<input>` element.
97
+ The `box` variable is a reference to the `<input>` element itself, which means we can
98
+ grab the input element's `value` and display it
112
99
with interpolation between `<p>` tags.
113
100
114
101
The template is completely self contained. It doesn't bind to the component,
115
102
and the component does nothing.
116
103
117
- Type something in the input box, and watch the display update with each keystroke.
104
+ Type in the input box, and watch the display update with each keystroke. *Voila!*
118
105
119
106
figure.image-display
120
107
img( src ='/resources/images/devguide/user-input/keyup-loop-back-anim.gif' alt ="loop back" )
121
108
.l-sub-section
122
109
:marked
123
- **This won't work unless you bind to an event**.
124
-
125
- Angular updates the bindings (and therefore the screen)
126
- only if the app does something in response to asynchronous events, such as keystrokes.
127
- This example code binds the `keyup` event
128
- to the number 0, the shortest template statement possible. The statement
129
- does nothing, but it satisfies Angular's requirement so that Angular will
130
- update the screen.
131
- :marked
132
- It's easier to get to the textbox with the template reference
133
- variable than to go through the `$event` object. Here's a rewrite of the previous
134
- ` keyup` example that uses a template reference variable to get the user's input.
110
+ **This won't work at all unless we bind to an event**.
111
+
112
+ Angular only updates the bindings (and therefore the screen)
113
+ if we do something in response to asynchronous events such as keystrokes.
114
+
115
+ That's why we bind the `keyup` event to a statement that does ... well, nothing.
116
+ We're binding to the number 0, the shortest statement we can think of.
117
+ That is all it takes to keep Angular happy. We said it would be clever!
118
+ :marked
119
+ That template reference variable is intriguing. It's clearly easier to get to the textbox with that
120
+ variable than to go through the `$event` object. Maybe we can rewrite our previous
121
+ keyup example so that it uses the variable to get the user's input. Let's give it a try .
135
122
+ makeExample('user-input/ts/app/keyup.components.ts' , 'key-up-component-2' ,'app/keyup.components.ts (v2)' )( format ="." )
136
123
:marked
137
- A nice aspect of this approach is that the component code gets clean data values from the view.
124
+ That sure seems easier.
125
+ An especially nice aspect of this approach is that our component code gets clean data values from the view.
138
126
It no longer requires knowledge of the `$event` and its structure.
139
127
140
128
<a id =" key-event" ></a >
141
129
.l-main-section
142
130
:marked
143
- ## Key event filtering (with `keyup.enter`)
144
- Sometimes only the Enter key matters, because it signals that the user has finished typing.
145
- But the `(keyup)` event handler hears *every keystroke.*
146
- One way to reduce the noise would be to examine every `$event.keyCode` and take action when the key is Enter.
147
- But there's an easier way: bind to Angular's `keyup.enter` pseudo-event. With this pseudo-event,
148
- Angular calls the event handler only when the user presses Enter.
131
+ ## Key event filtering (with `key.enter`)
132
+ Perhaps we don't care about every keystroke.
133
+ Maybe we're only interested in the input box value when the user presses Enter, and we'd like to ignore all other keys.
134
+ When we bind to the `(keyup)` event, our event handling statement hears *every keystroke*.
135
+ We could filter the keys first, examining every `$event.keyCode`, and update the `values` property only if the key is Enter.
149
136
150
- + makeExample('user-input/ts/app/keyup.components.ts' , 'key-up-component-3' ,'app/keyup.components.ts (v3)' )( format ="." )
137
+ Angular can filter the key events for us. Angular has a special syntax for keyboard events.
138
+ We can listen for just the Enter key by binding to Angular's `keyup.enter` pseudo-event.
151
139
152
- .l-sub-section
153
- : marked
154
- In this example, the data binding expression handles the event. It's a better practice
155
- to minimize JavaScript in HTML. Move this code to the component.
140
+ Only then do we update the component's `values` property. (In this example,
141
+ the update happens inside the event binding statement. A better practice
142
+ would be to put the update code in the component.)
143
+ + makeExample( 'user-input/ts/app/keyup.components.ts' , 'key-up-component-3' , 'app/keyup.components.ts (v3)' ) ( format = "." )
156
144
:marked
157
145
Here's how it works.
158
146
figure.image-display
@@ -162,62 +150,76 @@ figure.image-display
162
150
:marked
163
151
## On blur
164
152
165
- In the previous example, the current state of the input box
166
- is lost if the user mouses away and clicks elsewhere on the page
167
- without first pressing Enter.
168
- The component's `values` property is updated only when the user presses Enter.
153
+ Our previous example won't transfer the current state of the input box if the user mouses away and clicks
154
+ elsewhere on the page. We update the component's `values` property only when the user presses Enter
155
+ while the focus is inside the input box.
169
156
170
- To fix this issue, listen to both the Enter key and the blur event.
157
+ Let's fix that by listening to the input box's blur event as well .
171
158
172
159
+ makeExample('user-input/ts/app/keyup.components.ts' , 'key-up-component-4' ,'app/keyup.components.ts (v4)' )( format ="." )
173
160
174
161
.l-main-section
175
162
:marked
176
163
## Put it all together
177
- The previous page showed how to [display data](./displaying-data.html).
178
- This page demonstrated event binding techniques.
164
+ We learned how to [display data](./displaying-data.html) in the previous chapter .
165
+ We've acquired a small arsenal of event binding techniques in this chapter .
179
166
180
- Now, put it all together in a micro-app
181
- that can display a list of heroes and add new heroes to the list.
182
- The user can add a hero by typing the hero's name in the input box and
183
- clicking ** Add** .
167
+ Let's put it all together in a micro-app
168
+ that can display a list of heroes and add new heroes to that list.
169
+ The user can add a hero by first typing in the input box and then
170
+ pressing Enter, clicking the Add button, or clicking elsewhere on the page .
184
171
185
172
figure.image-display
186
173
img( src ='/resources/images/devguide/user-input/little-tour-anim.gif' alt ="Little Tour of Heroes" )
187
174
:marked
188
175
Below is the "Little Tour of Heroes" component.
176
+ We'll call out the highlights after we bask briefly in its minimalist glory.
189
177
190
178
+ makeExample('user-input/ts/app/little-tour.component.ts' , 'little-tour' , 'app/little-tour.component.ts' )( format ="." )
191
179
:marked
180
+ We've seen almost everything here before. A few things are new or bear repeating.
181
+
192
182
### Use template variables to refer to elements
193
183
194
184
The `newHero` template variable refers to the `<input>` element.
195
- You can use `newHero` from any sibling or child of the `<input>` element.
185
+ We can use `newHero` from any sibling or child of the `<input>` element.
196
186
197
187
Getting the element from a template variable makes the button click handler
198
- simpler. Without the variable, the code would have to include a CSS selector
188
+ simpler. Without the variable, we'd have to use a fancy CSS selector
199
189
to find the input element.
200
190
201
191
### Pass values, not elements
202
192
203
- Instead of passing the `newHero` into the component's `addHero` method,
204
- get the input box value and pass *that* to `addHero`.
193
+ We could have passed the `newHero` into the component's `addHero` method.
194
+
195
+ But that would require `addHero` to pick its way through the `<input>` DOM element,
196
+ something we learned to dislike in our first try at a [keyup component](#keyup1).
197
+
198
+ Instead, we grab the input box *value* and pass *that* to `addHero`.
199
+ The component knows nothing about HTML or the DOM, which is the way we like it.
205
200
206
201
### Keep template statements simple
207
- The `(blur)` event is bound to two JavaScript statements.
208
- The first statement calls `addHero`. The second statement, `newHero.value=''`,
209
- clears the input box after a new hero is added to the list.
202
+ We bound `(blur)` to *two* JavaScript statements.
203
+
204
+ We like the first one, which calls `addHero`.
205
+ We do not like the second one, which assigns an empty string to the input box value.
206
+
207
+ The second statement exists for a good reason. We have to clear the input box after adding the new hero to the list.
208
+ The component has no way to do that itself because it has no access to the
209
+ input box (our design choice).
210
+
211
+ Although the example *works*, we are rightly wary of JavaScript in HTML.
212
+ Template statements are powerful. We're supposed to use them responsibly.
213
+ Complex JavaScript in HTML is irresponsible.
210
214
211
- This works well enough for a demonstration, but
212
- it is better not to combine JavaScript and HTML.
213
- This example can be improved by using `NgModel`, as described in
214
- [Forms](./forms.html).
215
+ Should we reconsider our reluctance to pass the input box into the component?
215
216
217
+ There should be a better third way. And there is, as we'll see when we learn about `NgModel` in the [Forms](forms.html) chapter.
216
218
.l-main-section
217
219
:marked
218
220
## Source code
219
221
220
- Following is all the code discussed in this page .
222
+ Here is all the code we talked about in this chapter .
221
223
+ makeTabs(`
222
224
user-input/ts/app/click-me.component.ts,
223
225
user-input/ts/app/keyup.components.ts,
@@ -233,10 +235,10 @@ figure.image-display
233
235
:marked
234
236
## Summary
235
237
236
- You have mastered the basic primitives for responding to user input and gestures.
238
+ We've mastered the basic primitives for responding to user input and gestures.
239
+ As powerful as these primitives are, they are a bit clumsy for handling
240
+ large amounts of user input. We're operating down at the low level of events when
241
+ we should be writing two-way bindings between data entry fields and model properties.
237
242
238
- These techniques are useful for small-scale demonstrations, but they quickly become verbose and clumsy when handling
239
- large amounts of user input.
240
- Two-way data binding is a more elegant and compact way to move values between data entry fields and model properties.
241
- The next page, `Forms`, explains how to write
242
- two-way bindings with `NgModel`.
243
+ Angular has a two-way binding called `NgModel`, which we'll learn about
244
+ in the `Forms` chapter.
0 commit comments