parent
dc1c054927
commit
31051d29f0
|
@ -0,0 +1,27 @@
|
|||
<p>
|
||||
<click-me></click-me>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<click-me2></click-me2>
|
||||
</p>
|
||||
|
||||
<h4>Give me some keys!</h4>
|
||||
<div><key-up1></key-up1></div>
|
||||
|
||||
<h4>keyup loop-back component</h4>
|
||||
<div><loop-back></loop-back></div>
|
||||
<br><br>
|
||||
|
||||
<h4>Give me some more keys!</h4>
|
||||
<div><key-up2></key-up2></div>
|
||||
|
||||
<h4>Type away! Press [enter] when done.</h4>
|
||||
<div><key-up3></key-up3></div>
|
||||
|
||||
<h4>Type away! Press [enter] or click elsewhere when done.</h4>
|
||||
<div><key-up4></key-up4></div>
|
||||
|
||||
<h4>Little Tour of Heroes</h4>
|
||||
<p><i>Add a new hero</i></p>
|
||||
<div><little-tour></little-tour></div>
|
|
@ -0,0 +1,26 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
import {ClickMeComponent} from './click-me.component';
|
||||
import {ClickMeComponent2} from './click-me2.component';
|
||||
|
||||
import {LoopbackComponent} from './loop-back.component';
|
||||
|
||||
import {KeyUpComponent_v1,
|
||||
KeyUpComponent_v2,
|
||||
KeyUpComponent_v3,
|
||||
KeyUpComponent_v4} from './keyup.components';
|
||||
|
||||
import {LittleTourComponent} from './little-tour.component';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: 'app/app.component.html',
|
||||
directives: [
|
||||
ClickMeComponent, ClickMeComponent2,
|
||||
LoopbackComponent,
|
||||
KeyUpComponent_v1, KeyUpComponent_v2, KeyUpComponent_v3, KeyUpComponent_v4,
|
||||
LittleTourComponent
|
||||
]
|
||||
})
|
||||
export class AppComponent { }
|
|
@ -0,0 +1,4 @@
|
|||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {AppComponent} from './app.component';
|
||||
|
||||
bootstrap(AppComponent);
|
|
@ -0,0 +1,24 @@
|
|||
/* FOR DOCS ... MUST MATCH ClickMeComponent template
|
||||
// #docregion click-me-button
|
||||
<button (click)="onClickMe()">Click me!</button>
|
||||
// #enddocregion click-me-button
|
||||
*/
|
||||
|
||||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
// #docregion click-me-component
|
||||
@Component({
|
||||
selector: 'click-me',
|
||||
template: `
|
||||
<button (click)="onClickMe()">Click me!</button>
|
||||
{{clickMessage}}`
|
||||
})
|
||||
export class ClickMeComponent {
|
||||
clickMessage = '';
|
||||
|
||||
onClickMe(){
|
||||
this.clickMessage ='You are my hero!';
|
||||
}
|
||||
}
|
||||
// #enddocregion click-me-component
|
|
@ -0,0 +1,18 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
@Component({
|
||||
selector: 'click-me2',
|
||||
template: `
|
||||
<button (click)="onClickMe2($event)">No! .. Click me!</button>
|
||||
{{clickMessage}}`
|
||||
})
|
||||
export class ClickMeComponent2 {
|
||||
clickMessage = '';
|
||||
clicks = 1;
|
||||
|
||||
onClickMe2(event:any){
|
||||
let evtMsg = event ? ' Event target is '+ event.target.tagName : '';
|
||||
this.clickMessage = (`Click #${this.clicks++}. ${evtMsg}`)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
// #docregion key-up-component-1
|
||||
@Component({
|
||||
selector: 'key-up1',
|
||||
// #docregion key-up-component-1-template
|
||||
template: `
|
||||
<input (keyup)="onKey($event)">
|
||||
<p>{{values}}</p>
|
||||
`
|
||||
// #enddocregion key-up-component-1-template
|
||||
})
|
||||
// #docregion key-up-component-1-class, key-up-component-1-class-no-type
|
||||
export class KeyUpComponent_v1 {
|
||||
values='';
|
||||
|
||||
// #enddocregion key-up-component-1-class, key-up-component-1-class-no-type
|
||||
/*
|
||||
// #docregion key-up-component-1-class-no-type
|
||||
// without strong typing
|
||||
onKey(event:any) {
|
||||
this.values += event.target.value + ' | ';
|
||||
}
|
||||
// #enddocregion key-up-component-1-class-no-type
|
||||
*/
|
||||
// #docregion key-up-component-1-class
|
||||
// with strong typing
|
||||
onKey(event:KeyboardEvent) {
|
||||
this.values += (<HTMLInputElement>event.target).value + ' | ';
|
||||
}
|
||||
// #docregion key-up-component-1-class-no-type
|
||||
}
|
||||
// #enddocregion key-up-component-1,key-up-component-1-class, key-up-component-1-class-no-type
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
// #docregion key-up-component-2
|
||||
@Component({
|
||||
selector: 'key-up2',
|
||||
template: `
|
||||
<input #box (keyup)="onKey(box.value)">
|
||||
<p>{{values}}</p>
|
||||
`
|
||||
})
|
||||
export class KeyUpComponent_v2 {
|
||||
values='';
|
||||
onKey(value:string) {
|
||||
this.values += value + ' | ';
|
||||
}
|
||||
}
|
||||
// #enddocregion key-up-component-2
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
// #docregion key-up-component-3
|
||||
@Component({
|
||||
selector: 'key-up3',
|
||||
template: `
|
||||
<input #box (keyup.enter)="values=box.value">
|
||||
<p>{{values}}</p>
|
||||
`
|
||||
})
|
||||
export class KeyUpComponent_v3 {
|
||||
values='';
|
||||
}
|
||||
// #enddocregion key-up-component-3
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
// #docregion key-up-component-4
|
||||
@Component({
|
||||
selector: 'key-up4',
|
||||
template: `
|
||||
<input #box
|
||||
(keyup.enter)="values=box.value"
|
||||
(blur)="values=box.value">
|
||||
|
||||
<p>{{values}}</p>
|
||||
`
|
||||
})
|
||||
export class KeyUpComponent_v4 {
|
||||
values='';
|
||||
}
|
||||
// #enddocregion key-up-component-4
|
|
@ -0,0 +1,25 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
// #docregion little-tour
|
||||
@Component({
|
||||
selector: 'little-tour',
|
||||
template: `
|
||||
<input #newHero
|
||||
(keyup.enter)="addHero(newHero.value)"
|
||||
(blur)="addHero(newHero.value); newHero.value='' ">
|
||||
|
||||
<button (click)=addHero(newHero.value)>Add</button>
|
||||
|
||||
<ul><li *ngFor="#hero of heroes">{{hero}}</li></ul>
|
||||
`
|
||||
})
|
||||
export class LittleTourComponent {
|
||||
heroes=['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
|
||||
addHero(newHero:string) {
|
||||
if (newHero) {
|
||||
this.heroes.push(newHero);
|
||||
}
|
||||
}
|
||||
}
|
||||
// #enddocregion little-tour
|
|
@ -0,0 +1,12 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
// #docregion loop-back-component
|
||||
@Component({
|
||||
selector: 'loop-back',
|
||||
template:`
|
||||
<input #box (keyup)="0">
|
||||
<p>{{box.value}}</p>
|
||||
`
|
||||
})
|
||||
export class LoopbackComponent { }
|
||||
// #enddocregion loop-back-component
|
|
@ -2,9 +2,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>User Input</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script src="../node_modules/systemjs/dist/system.src.js"></script>
|
||||
<script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||
<script>
|
||||
System.config({
|
||||
packages: {'app': {defaultExtension: 'js'}}
|
||||
|
@ -15,8 +15,6 @@
|
|||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
<hr>
|
||||
<little-tour>Loading...</little-tour>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"description": "User Input",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags": ["input"]
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
<!-- #docregion click-me-button -->
|
||||
<button (click)="onClickMe()">Click me!</button>
|
||||
<!-- #enddocregion click-me-button -->
|
||||
|
||||
<h4>keyup loop-back component</h4>
|
||||
<loop-back></loop-back>
|
||||
|
||||
<key-up></key-up>
|
||||
|
||||
<key-up2></key-up2>
|
||||
|
||||
<key-up3></key-up3>
|
||||
|
||||
<key-up4></key-up4>
|
|
@ -1,147 +0,0 @@
|
|||
// #docplaster
|
||||
|
||||
// imports formatted for dev guide only
|
||||
// #docregion little-tour-of-heroes-app
|
||||
import {bootstrap, Component, CORE_DIRECTIVES} from 'angular2/angular2';
|
||||
|
||||
// #enddocregion little-tour-of-heroes-app
|
||||
|
||||
|
||||
// #docregion click-me-component
|
||||
@Component({
|
||||
selector: 'click-me',
|
||||
template: '<button (click)="onClickMe()">Click me</button>'
|
||||
})
|
||||
class ClickMeComponent {
|
||||
onClickMe(){
|
||||
alert('You are my hero!')
|
||||
}
|
||||
}
|
||||
// #enddocregion click-me-component
|
||||
|
||||
// #docregion loop-back-component
|
||||
@Component({
|
||||
selector: 'loop-back',
|
||||
template: '<input #box (keyup)="0"> <p>{{box.value}}</p>'
|
||||
})
|
||||
class LoopbackComponent {
|
||||
}
|
||||
// #enddocregion loop-back-component
|
||||
|
||||
// #docregion key-up-component
|
||||
@Component({
|
||||
selector: 'key-up',
|
||||
template: `
|
||||
<h4>Give me some keys!</h4>
|
||||
<div><input (keyup)="onKey($event)"></div>
|
||||
<div>{{values}}</div>
|
||||
`
|
||||
})
|
||||
class KeyUpComponent {
|
||||
values='';
|
||||
onKey(event) {
|
||||
this.values += event.target.value + ' | ';
|
||||
}
|
||||
}
|
||||
// #enddocregion key-up-component
|
||||
|
||||
// #docregion key-up2-component
|
||||
@Component({
|
||||
selector: 'key-up2',
|
||||
template: `
|
||||
<h4>Give me some more keys!</h4>
|
||||
<div><input #box (keyup)="onKey(box.value)"></div>
|
||||
<div>{{values}}</div>
|
||||
`
|
||||
})
|
||||
class KeyUpComponentV2 {
|
||||
values='';
|
||||
onKey(value) {
|
||||
this.values += value + ' | ';
|
||||
}
|
||||
}
|
||||
// #enddocregion key-up2-component
|
||||
|
||||
|
||||
// #docregion key-up3-component
|
||||
@Component({
|
||||
selector: 'key-up3',
|
||||
template: `
|
||||
<h4>Type away! Press [enter] when done.</h4>
|
||||
<div><input #box (keyup.enter)="values=box.value"></div>
|
||||
<div>{{values}}</div>
|
||||
`
|
||||
})
|
||||
class KeyUpComponentV3 {
|
||||
values='';
|
||||
}
|
||||
// #enddocregion key-up3-component
|
||||
|
||||
|
||||
// #docregion key-up4-component
|
||||
@Component({
|
||||
selector: 'key-up4',
|
||||
template: `
|
||||
<h4>Type away! Press [enter] or mouse away when done.</h4>
|
||||
<div>
|
||||
<input #box
|
||||
(keyup.enter)="values=box.value"
|
||||
(blur)="values=box.value">
|
||||
<div>
|
||||
<div>{{values}}</div>
|
||||
`
|
||||
})
|
||||
class KeyUpComponentV4 {
|
||||
values='';
|
||||
}
|
||||
// #enddocregion key-up4-component
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: 'app/app.html',
|
||||
directives: [
|
||||
CORE_DIRECTIVES,
|
||||
ClickMeComponent,
|
||||
KeyUpComponent, KeyUpComponentV2, KeyUpComponentV3, KeyUpComponentV4,
|
||||
LoopbackComponent,
|
||||
]
|
||||
})
|
||||
class AppComponent {
|
||||
|
||||
onClickMe(event){
|
||||
let evtMsg = event ? ' Event target class is '+ event.target.className : '';
|
||||
alert('Click me.'+evtMsg)
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap(AppComponent);
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
// #docregion little-tour-of-heroes-app
|
||||
@Component({
|
||||
selector: 'little-tour',
|
||||
template: `
|
||||
<h4>Little Tour of Heroes</h4>
|
||||
<input #new-hero
|
||||
(keyup.enter)="addHero(newHero)"
|
||||
(blur)="addHero(newHero)">
|
||||
<button (click)=addHero(newHero)>Add</button>
|
||||
<ul><li *ng-for="#hero of heroes">{{hero}}</li></ul>
|
||||
`,
|
||||
directives: [CORE_DIRECTIVES]
|
||||
})
|
||||
class LittleTour {
|
||||
heroes=['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
|
||||
|
||||
addHero(newHero) {
|
||||
if (newHero.value) {
|
||||
this.heroes.push(newHero.value);
|
||||
newHero.value = null; // clear the newHero textbox
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap(LittleTour);
|
||||
// #enddocregion little-tour-of-heroes-app
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"files": ["!*.json"]
|
||||
}
|
|
@ -5,7 +5,7 @@ include ../../../../_includes/_util-fns
|
|||
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.
|
||||
|
||||
[Live Example](/resources/live-examples/user-input/ts/src/plnkr.html).
|
||||
[Live Example](/resources/live-examples/user-input/ts/plnkr.html).
|
||||
|
||||
:marked
|
||||
## Binding to User Input Events
|
||||
|
@ -15,7 +15,7 @@ include ../../../../_includes/_util-fns
|
|||
|
||||
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.
|
||||
+makeExample('user-input/ts/src/app/app.html', 'click-me-button')(format=".")
|
||||
+makeExample('user-input/ts/app/click-me.component.ts', 'click-me-button')(format=".", language="html")
|
||||
|
||||
<a id="click"></a>
|
||||
:marked
|
||||
|
@ -28,10 +28,8 @@ include ../../../../_includes/_util-fns
|
|||
The identifers appearing within an expression belong to a specific context object.
|
||||
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:
|
||||
<!--
|
||||
These sample can be found in http://plnkr.co/edit/mr63T5
|
||||
-->
|
||||
+makeExample('user-input/ts/src/app/app.ts', 'click-me-component')
|
||||
|
||||
+makeExample('user-input/ts/app/click-me.component.ts', 'click-me-component', 'app/click-me.component.ts')
|
||||
: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.
|
||||
|
@ -42,27 +40,43 @@ include ../../../../_includes/_util-fns
|
|||
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.
|
||||
|
||||
This time we'll both listen to an event and grab the user's input.
|
||||
+makeExample('user-input/ts/src/app/app.ts', 'key-up-component')
|
||||
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)')
|
||||
:marked
|
||||
Angular makes an event object available in the **`$event`** variable
|
||||
which we pass to the component's `onKey()` method.
|
||||
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)')
|
||||
:marked
|
||||
Angular makes an event object available in the **`$event`** variable. The user data we want is in that variable somewhere.
|
||||
|
||||
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 `$event.target` gives us the
|
||||
[`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.
|
||||
|
||||
With had this in mind when we passed `$event` to our `onKey()` component method. That method extracts the user's input and
|
||||
concatenates it to the previous user data that we're accumulating in the component's' `values` property.
|
||||
With had this in mind when we passed `$event` to our `onKey()` component method where we extract the user's input and
|
||||
concatenate it to the previous user data that we're accumulating in the component's' `values` property.
|
||||
We then use [interpolation](./template-syntax.html#interpolation)
|
||||
to display the `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:
|
||||
code-example().
|
||||
a | ab | abc | ab | a | |
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/user-input/keyup1-anim.gif' alt="key up 1")
|
||||
|
||||
<a id="keyup1"></a>
|
||||
.l-sub-section
|
||||
:marked
|
||||
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.
|
||||
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 )')
|
||||
:marked
|
||||
<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.
|
||||
|
||||
We'll address this problem in our next try at processing user keystrokes.
|
||||
:marked
|
||||
|
||||
.l-main-section
|
||||
|
@ -74,28 +88,35 @@ figure.image-display
|
|||
These variables grant us direct access to an element.
|
||||
We declare a local template variable by preceding an identifier with a hash/pound character (#).
|
||||
|
||||
Let's demonstrate with a clever keystroke loopback in a single line of template HTML.
|
||||
We don't actually need a dedicated component to do this but we'll make one anyway.
|
||||
+makeExample('user-input/ts/src/app/app.ts', 'loop-back-component')
|
||||
Let's demonstrate with 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')
|
||||
:marked
|
||||
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
|
||||
grab the input element's `value` and display it
|
||||
with interpolation between `<p>` tags. The display updates as we type. *Voila!*
|
||||
|
||||
**This won't work at all unless we bind to an event**. Angular only updates the bindings
|
||||
(and therefore the screen)
|
||||
if we do something in response to asynchronous events such as keystrokes.
|
||||
|
||||
In this silly example we aren't really interested in the event at all.
|
||||
But an Event Binding requires a template expression to evaluate when the event fires.
|
||||
Many things qualify as expressions, none simpler than a one-character literal
|
||||
like the number zero. That's all it takes to keep Angular happy. We said it would be clever!
|
||||
|
||||
with interpolation between `<p>` tags.
|
||||
|
||||
The template is completely self contained. It doesn't bind to the component which does nothing.
|
||||
|
||||
Type in the input box and watch the display update with each keystroke. *Voila!*
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/user-input/keyup-loop-back-anim.gif' alt="loop back")
|
||||
.l-sub-section
|
||||
:marked
|
||||
**This won't work at all unless we bind to an event**.
|
||||
|
||||
Angular only updates the bindings (and therefore the screen)
|
||||
if we do something in response to asynchronous events such as keystrokes.
|
||||
|
||||
That's why we bind the `keyup` event to an expression that does ... well, nothing.
|
||||
We're binding to the number 0, the shortest expression we can think of.
|
||||
That is all it takes to keep Angular happy. We said it would be clever!
|
||||
:marked
|
||||
That local template variable is intriguing. It's clearly easer to get to the textbox with that
|
||||
variable than to go through the `$event` object. Maybe we can re-write our previous
|
||||
"key-up" example using the variable to acquire the user's' input. Let's give it a try.
|
||||
+makeExample('user-input/ts/src/app/app.ts', 'key-up2-component')
|
||||
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-2' ,'app/keyup.components.ts (v2)')
|
||||
:marked
|
||||
That sure seems easier.
|
||||
An especially nice aspect of this approach is that our component code gets clean data values from the view.
|
||||
|
@ -115,8 +136,10 @@ figure.image-display
|
|||
|
||||
Only then do we update the component's `values` property ...
|
||||
inside the event expression rather than in the component ...
|
||||
because we can ... even if it is a dubious practice.
|
||||
+makeExample('user-input/ts/src/app/app.ts', 'key-up3-component')
|
||||
because we *can* ... even if it is a dubious practice.
|
||||
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-3' ,'app/keyup.components.ts (v3)')
|
||||
:marked
|
||||
Here's how it works.
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/user-input/keyup3-anim.gif' alt="key up 3")
|
||||
|
||||
|
@ -130,7 +153,7 @@ figure.image-display
|
|||
|
||||
Let's fix that by listening to the input box's blur event as well.
|
||||
|
||||
+makeExample('user-input/ts/src/app/app.ts', 'key-up4-component')
|
||||
+makeExample('user-input/ts/app/keyup.components.ts', 'key-up-component-4' ,'app/keyup.components.ts (v4)')
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
|
@ -146,43 +169,62 @@ figure.image-display
|
|||
figure.image-display
|
||||
img(src='/resources/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes")
|
||||
:marked
|
||||
Below is the entire "Little Tour of Heroes" micro-app in a single component.
|
||||
Below is the "Little Tour of Heroes" component.
|
||||
We'll call out the highlights after we bask briefly in its minimalist glory.
|
||||
<!--
|
||||
This example in http://plnkr.co/edit/JWeIqq
|
||||
-->
|
||||
+makeExample('user-input/ts/src/app/app.ts', 'little-tour-of-heroes-app')
|
||||
|
||||
+makeExample('user-input/ts/app/little-tour.component.ts', 'little-tour', 'app/little-tour.component.ts')
|
||||
:marked
|
||||
We've seen almost everything here before. A few things are new or bear repeating.
|
||||
|
||||
### **Beware of camelCase variable names**
|
||||
We enter new hero names in the `<input>` element so we chose `newHero` to be the name of the local template variable.
|
||||
|
||||
Unfortunately, we can't use that name when we declare the variable with (#).
|
||||
The browser forces all attribute and element names to lowercase, turning what would be `#newHero`
|
||||
into `#newhero` (all lowercase). We don't want a `newhero` variable name in our template expressions.
|
||||
|
||||
The Angular workaround is to spell the declaration in "kebab case". Angular translates "#new-hero"
|
||||
to `newHero` for template expressions ... which is exactly what we want.
|
||||
|
||||
### **newHero refers to the `<input>` element**
|
||||
We can access the `newHero` variable from any sibling or child of the `<input>` element.
|
||||
### *newHero* template variable
|
||||
|
||||
The *newHero* template variable refers to the `<input>` element.
|
||||
|
||||
We can access `newHero` from any sibling or child of the `<input>` element.
|
||||
When the user clicks the button, we don't need a fancy css selector to
|
||||
track down the textbox and extract its value.
|
||||
The button simply passes the `newHero` textbox reference to its own click handling method.
|
||||
That's a tremendous simplification, as anyone who's wrangled jQuery can confirm.
|
||||
|
||||
Ready access to the `<input>` element also makes it easy for the `addHero` method
|
||||
to clear the textbox after processing the new hero.
|
||||
|
||||
### **The *ng-for repeater**
|
||||
The `ng-for` directive repeats the template as many times as there are heroes in the `heroes` list.
|
||||
We must remember to list `NgFor` among the directives used by the component's template
|
||||
by importing the `CORE_DIRECTIVES` constant and adding it to the
|
||||
@Component decorator's `directives` array.
|
||||
|
||||
We learned about `NgFor` in the "[Displaying Data](displaying-data.html#ng-for)" chapter.
|
||||
|
||||
### Extract the input box *value*
|
||||
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,
|
||||
something we learned to dislike in our first try at a [*KeyupComponent*](#keyup1).
|
||||
|
||||
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.
|
||||
|
||||
### Don't let template expressions be complex
|
||||
We bound `(blur)` to *two* JavaScript statements.
|
||||
|
||||
We like the first one that calls `addHero`.
|
||||
We do not like the second one that 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 component has no way to do that itself — because it has no access to the
|
||||
input box (our design choice).
|
||||
|
||||
Although it *works*, we are rightly wary of JavaScript in HTML.
|
||||
Template expressions are powerful. We're supposed to use them responsibly.
|
||||
Complex JavaScript in HTML is irresponsible.
|
||||
|
||||
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.
|
||||
.l-main-section
|
||||
:marked
|
||||
## Source code
|
||||
|
||||
Here is all the code we talked about in this chapter.
|
||||
+makeTabs(`
|
||||
user-input/ts/app/click-me.component.ts,
|
||||
user-input/ts/app/keyup.components.ts,
|
||||
user-input/ts/app/loop-back.component.ts,
|
||||
user-input/ts/app/little-tour.component.ts
|
||||
`,'',
|
||||
`click-me.component.ts,
|
||||
keyup.components.ts,
|
||||
loop-back.component.ts,
|
||||
little-tour.component.ts`)
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Loading…
Reference in New Issue