docs(user-input): copyedit TS user-input

closes #665
This commit is contained in:
Kathy Walrath 2016-01-07 14:48:15 -08:00 committed by Ward Bell
parent 1fc7df82b5
commit 693c5e60e6
1 changed files with 70 additions and 65 deletions

View File

@ -1,26 +1,27 @@
include ../../../../_includes/_util-fns include ../../../../_includes/_util-fns
:marked :marked
When the user clicks a link, pushes a button, or types on the keyboard When the user clicks a link, pushes a button, or enters text
we want to know about it. These user actions all raise DOM events. we want to know about it. These user actions all raise DOM events.
In this chapter we learn to bind to those events using the Angular Event Binding syntax. In this chapter we learn to bind to those events using the Angular
event binding syntax.
[Live Example](/resources/live-examples/user-input/ts/plnkr.html). [Run the live example](/resources/live-examples/user-input/ts/plnkr.html)
:marked :marked
## Binding to User Input Events ## Binding to user input events
We can listen to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events) We can use [Angular event bindings](./template-syntax.html#event-binding)
with an [Angular Event Binding](./template-syntax.html#event-binding). to respond to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events).
The syntax is simple. We assign a template expression to the DOM event name, surrounded in parentheses. The syntax is simple. We assign a template expression to the DOM event name, surrounded in parentheses.
A click Event Binding makes for a quick illustration. As an example, here's an event binding that implements a click handler:
+makeExample('user-input/ts/app/click-me.component.ts', 'click-me-button')(format=".", language="html") +makeExample('user-input/ts/app/click-me.component.ts', 'click-me-button')(format=".", language="html")
<a id="click"></a> <a id="click"></a>
:marked :marked
The `(click)` to the left of the equal sign identifies the button's click event as the **target of the binding**. The `(click)` to the left of the equal sign identifies the button's click event as the **target of the binding**.
The text within quotes on the right is the "**template expression**" in which we The text within quotes on the right is the **template expression** in which we
respond to the click event by calling the component's `onClickMe` method. A [template expression](./template-syntax.html#template-expressions) is a subset respond to the click event by calling the component's `onClickMe` method. A [template expression](./template-syntax.html#template-expressions) is a subset
of JavaScript with a few added tricks. of JavaScript with a few added tricks.
@ -29,37 +30,37 @@ include ../../../../_includes/_util-fns
That object is usually the Angular component that controls the template ... which it definitely is That object is usually the Angular component that controls the template ... which it definitely is
in this case because that snippet of HTML belongs to the following component: in this case because that snippet of HTML belongs to the following component:
+makeExample('user-input/ts/app/click-me.component.ts', 'click-me-component', 'app/click-me.component.ts') +makeExample('user-input/ts/app/click-me.component.ts', 'click-me-component', 'app/click-me.component.ts')(format=".")
:marked :marked
The `onClickMe` in the template refers to the `onClickMe` method of the component.
When the user clicks the button, Angular calls the component's `onClickMe` method. When the user clicks the button, Angular calls the component's `onClickMe` method.
.l-main-section .l-main-section
:marked :marked
## Get user input from the $event object ## Get user input from the $event object
We can bind to all kinds of events. Let's bind to the "keyup" event of an input box and replay We can bind to all kinds of events. Let's bind to the keyup event of an input box and replay
what the user types back onto the screen. what the user types back onto the screen.
This time we'll (1) listen to an event and (2) grab the user's input. This time we'll (1) listen to an event and (2) grab the user's input.
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-template', 'app/keyup.components.ts (template v.1)') +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-template', 'app/keyup.components.ts (template v.1)')(format=".")
:marked :marked
Angular makes an event object available in the **`$event`** variable Angular makes an event object available in the **`$event`** variable,
which we pass to the component's `onKey()` method. which we pass to the component's `onKey()` method.
The user data we want is in that variable somewhere. The user data we want is in that variable somewhere.
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class-no-type', 'app/keyup.components.ts (class v.1)') +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class-no-type', 'app/keyup.components.ts (class v.1)')(format=".")
:marked :marked
The shape of the `$event` object is determined by whatever raises the event. The shape of the `$event` object is determined by whatever raises the event.
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). 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).
The `$event.target` gives us the The `$event.target` gives us an
[`HTMLInputElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement) which [`HTMLInputElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement), which
has a `value` property and that's where we find our user input data. has a `value` property that contains our user input data.
We had this in mind when we passed `$event` to our `onKey()` component method. This is where we extract the user's input and The `onKey()` component method is where we extract the user's input
concatenate it to the previous user data that we're accumulating in the component's `values` property. from the event object, adding that input to the list of user data that we're accumulating in the component's `values` property.
We then use [interpolation](./template-syntax.html#interpolation) We then use [interpolation](./template-syntax.html#interpolation)
to display the accumulating `values` property back on screen. to display the accumulating `values` property back on screen.
Enter the letters "abc", backspace to remove them, and we should see: Enter the letters "abc", and then backspace to remove them.
Here's what the UI displays:
code-example(). code-example().
a | ab | abc | ab | a | | a | ab | abc | ab | a | |
figure.image-display figure.image-display
@ -68,10 +69,10 @@ figure.image-display
<a id="keyup1"></a> <a id="keyup1"></a>
.l-sub-section .l-sub-section
:marked :marked
We cast the `$event` as an `any` type which means we've abandoned strong typing We cast the `$event` as an `any` type, which means we've abandoned strong typing
to simplify our code. We generally prefer the strong typing that TypeScript affords. to simplify our code. We generally prefer the strong typing that TypeScript affords.
We can rewrite the method, casting to HTML DOM objects like this. We can rewrite the method, casting to HTML DOM objects like this.
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class', 'app/keyup.components.ts (class v.1 - strongly typed )') +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-1-class', 'app/keyup.components.ts (class v.1 - strongly typed )')(format=".")
:marked :marked
<br>Strong typing reveals a serious problem with passing a DOM event into the method: <br>Strong typing reveals a serious problem with passing a DOM event into the method:
too much awareness of template details, too little separation of concerns. too much awareness of template details, too little separation of concerns.
@ -88,17 +89,19 @@ figure.image-display
These variables grant us direct access to an element. These variables grant us direct access to an element.
We declare a local template variable by preceding an identifier with a hash/pound character (#). We declare a local template variable by preceding an identifier with a hash/pound character (#).
Let's demonstrate with a clever keystroke loopback in an ultra-simple template. Here's an example of using a local template variable
+makeExample('user-input/ts/app/loop-back.component.ts', 'loop-back-component', 'app/loop-back.component.ts') to implement a clever keystroke loopback in an ultra-simple template.
+makeExample('user-input/ts/app/loop-back.component.ts', 'loop-back-component', 'app/loop-back.component.ts')(format=".")
:marked :marked
We've declared a template local variable named `box` on the `<input>` element. We've declared a template local variable named `box` on the `<input>` element.
The `box` variable is a reference to the `<input>` element itself which means we can The `box` variable is a reference to the `<input>` element itself, which means we can
grab the input element's `value` and display it grab the input element's `value` and display it
with interpolation between `<p>` tags. with interpolation between `<p>` tags.
The template is completely self contained. It doesn't bind to the component which does nothing. The template is completely self contained. It doesn't bind to the component,
and the component does nothing.
Type in the input box and watch the display update with each keystroke. *Voila!* Type in the input box, and watch the display update with each keystroke. *Voila!*
figure.image-display figure.image-display
img(src='/resources/images/devguide/user-input/keyup-loop-back-anim.gif' alt="loop back") img(src='/resources/images/devguide/user-input/keyup-loop-back-anim.gif' alt="loop back")
@ -114,9 +117,9 @@ figure.image-display
That is all it takes to keep Angular happy. We said it would be clever! That is all it takes to keep Angular happy. We said it would be clever!
:marked :marked
That local template variable is intriguing. It's clearly easier to get to the textbox with that That local template variable is intriguing. It's clearly easier to get to the textbox with that
variable than to go through the `$event` object. Maybe we can re-write our previous variable than to go through the `$event` object. Maybe we can rewrite our previous
"key-up" example using the variable to acquire the user's input. Let's give it a try. keyup example so that it uses the variable to get the user's input. Let's give it a try.
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-2' ,'app/keyup.components.ts (v2)') +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-2' ,'app/keyup.components.ts (v2)')(format=".")
:marked :marked
That sure seems easier. That sure seems easier.
An especially nice aspect of this approach is that our component code gets clean data values from the view. An especially nice aspect of this approach is that our component code gets clean data values from the view.
@ -127,17 +130,17 @@ figure.image-display
:marked :marked
## Key event filtering (with `key.enter`) ## Key event filtering (with `key.enter`)
Perhaps we don't care about every keystroke. Perhaps we don't care about every keystroke.
We're only interested in the input box value when the user hits the "Enter" key. We'd like to ignore all other keys. Maybe we're only interested in the input box value when the user presses Enter, and we'd like to ignore all other keys.
When we bind to the `(keyup)` event, our event handling expression hears *every key stroke*. When we bind to the `(keyup)` event, our event handling expression hears *every keystroke*.
We could filter the keys first, examining every `$event.keyCode`, and update the `values` property only if the key is "Enter". We could filter the keys first, examining every `$event.keyCode`, and update the `values` property only if the key is Enter.
Angular can filter the key events for us. Angular has a special syntax for keyboard events. Angular can filter the key events for us. Angular has a special syntax for keyboard events.
We can listen for just the "Enter" key by binding to Angular's `keyup.enter` pseudo-event. We can listen for just the Enter key by binding to Angular's `keyup.enter` pseudo-event.
Only then do we update the component's `values` property ... Only then do we update the component's `values` property. (In this example,
inside the event expression rather than in the component ... the update happens inside the event expression. A better practice
because we *can* ... even if it is a dubious practice. would be to put the update code in the component.)
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-3' ,'app/keyup.components.ts (v3)') +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-3' ,'app/keyup.components.ts (v3)')(format=".")
:marked :marked
Here's how it works. Here's how it works.
figure.image-display figure.image-display
@ -148,12 +151,12 @@ figure.image-display
## On blur ## On blur
Our previous example won't transfer the current state of the input box if the user mouses away and clicks Our previous example won't transfer the current state of the input box if the user mouses away and clicks
elsewhere on the page. We only update the component's `values` property when the user presses "Enter" elsewhere on the page. We update the component's `values` property only when the user presses Enter
inside the input box. while the focus is inside the input box.
Let's fix that by listening to the input box's blur event as well. Let's fix that by listening to the input box's blur event as well.
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-4' ,'app/keyup.components.ts (v4)') +makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-4' ,'app/keyup.components.ts (v4)')(format=".")
.l-main-section .l-main-section
:marked :marked
@ -162,9 +165,9 @@ figure.image-display
We've acquired a small arsenal of event binding techniques in this chapter. We've acquired a small arsenal of event binding techniques in this chapter.
Let's put it all together in a micro-app Let's put it all together in a micro-app
that can display a list of heroes and add new heroes to that list that can display a list of heroes and add new heroes to that list.
by typing in the input box and hitting "Enter", clicking "Add", or clicking The user can add a hero by first typing in the input box and then
elsewhere on the page. pressing Enter, clicking the Add button, or clicking elsewhere on the page.
figure.image-display figure.image-display
img(src='/resources/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes") img(src='/resources/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes")
@ -172,44 +175,46 @@ figure.image-display
Below is the "Little Tour of Heroes" component. Below is the "Little Tour of Heroes" component.
We'll call out the highlights after we bask briefly in its minimalist glory. We'll call out the highlights after we bask briefly in its minimalist glory.
+makeExample('user-input/ts/app/little-tour.component.ts', 'little-tour', 'app/little-tour.component.ts') +makeExample('user-input/ts/app/little-tour.component.ts', 'little-tour', 'app/little-tour.component.ts')(format=".")
:marked :marked
We've seen almost everything here before. A few things are new or bear repeating. We've seen almost everything here before. A few things are new or bear repeating.
### *newHero* template variable ### Use template variables to refer to elements
The *newHero* template variable refers to the `<input>` element. The `newHero` template variable refers to the `<input>` element.
We can use `newHero` from any sibling or child of the `<input>` element.
We can access `newHero` from any sibling or child of the `<input>` element. Getting the element from a template variable makes the button click handler
When the user clicks the button, we don't need a fancy CSS selector to simpler. Without the variable, we'd have to use a fancy CSS selector
track down the textbox and extract its value. to find the input element.
### Extract the input box *value* ### Pass values, not elements
We could have passed the `newHero` into the component's `addHero()` method.
We could have passed the `newHero` into the component's `addHero` method.
But that would require `addHero` to pick its way through the `<input>` DOM element, But that would require `addHero` to pick its way through the `<input>` DOM element,
something we learned to dislike in our first try at a [*KeyupComponent*](#keyup1). something we learned to dislike in our first try at a [keyup component](#keyup1).
Instead, we grab the input box *value* and pass *that* to `addHero()`. Instead, we grab the input box *value* and pass *that* to `addHero`.
The component knows nothing about HTML or DOM which is the way we like it. The component knows nothing about HTML or the DOM, which is the way we like it.
### Don't let template expressions be complex ### Keep template expressions simple
We bound `(blur)` to *two* JavaScript statements. We bound `(blur)` to *two* JavaScript statements.
We like the first one that calls `addHero`. We like the first one, which calls `addHero`.
We do not like the second one that assigns an empty string to the input box value. We do not like the second one, which assigns an empty string to the input box value.
We did it for a good reason. We have to clear the input box after adding the new hero to the list. The second statement exists for a good reason. We have to clear the input box after adding the new hero to the list.
The component has no way to do that itself &mdash; because it has no access to the The component has no way to do that itself because it has no access to the
input box (our design choice). input box (our design choice).
Although it *works*, we are rightly wary of JavaScript in HTML. Although the example *works*, we are rightly wary of JavaScript in HTML.
Template expressions are powerful. We're supposed to use them responsibly. Template expressions are powerful. We're supposed to use them responsibly.
Complex JavaScript in HTML is irresponsible. Complex JavaScript in HTML is irresponsible.
Should we reconsider our reluctance to pass the input box into the component? Should we reconsider our reluctance to pass the input box into the component?
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. 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.
.l-main-section .l-main-section
:marked :marked
## Source code ## Source code
@ -228,12 +233,12 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## Next Steps ## Summary
We've mastered the basic primitives for responding to user input and gestures. We've mastered the basic primitives for responding to user input and gestures.
As powerful as these primitives are, they are a bit clumsy for handling As powerful as these primitives are, they are a bit clumsy for handling
large amounts of user input. We're operating down at the low level of events when large amounts of user input. We're operating down at the low level of events when
we should be writing two-way bindings between data entry fields and model properties. we should be writing two-way bindings between data entry fields and model properties.
Angular has a two-way binding called `NgModel` and we learn about it Angular has a two-way binding called `NgModel`, which we'll learn about
in the `Forms` chapter. in the `Forms` chapter.