目录
显示数据
在 Angular 中最典型的数据显示方式,就是把 HTML 模板中的控件绑定到 Angular 组件的属性。
使用插值表达式显示组件属性
- 要显示组件的属性,最简单的方式就是通过插值表达式 (interpolation) 来绑定属性名。 要使用插值表达式,就把属性名包裹在双花括号里放进视图模板,如{{myHero}}。
- 模板是包在 ECMAScript 2015 反引号 (
) 中的一个多行字符串。 反引号 (
) — 注意,不是单引号 (‘) — 允许把一个字符串写在多行上, 使 HTML 模板更容易阅读。
用户输入
用户输入触发 DOM 事件。我们通过事件绑定来监听它们,把更新过的数据导入回我们的组件和 model。
1. 要绑定 DOM 事件,只要把 DOM 事件的名字包裹在圆括号中,然后用放在引号中的模板语句对它赋值就可以了。
2. 写绑定时,需要知道模板语句的执行上下文。 出现在模板语句中的每个标识符都属于特定的上下文对象。 这个对象通常都是控制此模板的 Angular 组件。
3. DOM 事件可以携带可能对组件有用的信息。如:通过
5. 当用户按下并释放一个按键时,触发keyup事件,Angular 在$event变量提供一个相应的 DOM 事件对象。在组件中使用event.target.value
可获取输入的值。
6. 所有标准 DOM 事件对象都有一个target属性, 引用触发该事件的元素。
模板引用变量
还有另一种获取用户数据的方式:使用 Angular 的模板引用变量。 这些变量提供了从模块中直接访问元素的能力。 在标识符前加上井号 (#) 就能声明一个模板引用变量。
@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 + ' | ';
}
}
按键事件过滤
- (keyup)事件处理器监听每一次按键。 有时只在意回车键,因为它标志着用户结束输入。 解决这个问题的一种方法是检查每个$event.keyCode,只有键值是回车键时才采取行动。
- 更简单的方法是:绑定到 Angular 的keyup.enter 模拟事件。 然后,只有当用户敲回车键时,Angular 才会调用事件处理器。
表单
模板驱动的表单
- 如果组件、指令或管道出现在模块的imports数组中,不要把它声明在declarations数组中。 如果它是你自己写的,并且属于当前模块,就要把它声明在declarations数组中。
- 当在表单中使用[(ngModel)]时,必须要定义name属性。
- 在内部,Angular 创建了一些FormControl,并把它们注册到
NgForm
指令,再将该指令附加到标签。 注册每个FormControl时,使用name属性值作为键值。 - 每个 input 元素都有
name
属性,Angular 表单用它注册控件。 - 在属性绑定中,值从模型中流动到屏幕上的目标属性 (property)。 通过把属性名括在方括号中来标记出目标属性,[]。 这是从模型到视图的单向数据绑定。
- 在事件绑定中,值从屏幕上的目标属性流动到模型。 通过把属性名括在圆括号中来标记出目标属性,()。 这是从视图到模型的反向单向数据绑定。
- Angular 选择了组合标点 [()] 来标记出双向数据绑定和双向数据流。
- 事实上,可以把NgModel绑定拆成两个独立的绑定,如:
<input type="text" class="form-control" id="name" required [ngModel]="model.name" name="name" (ngModelChange)="model.name = $event" >
。
其中:ngModelChange
并不是元素的事件。 它实际上是来自NgModel指令的事件属性。 - 当 Angular 在表单中看到[(x)]的绑定目标时, 它会期待这个x指令有一个名为x的输入属性,和一个名为xChange的输出属性。
- 模板表达式中的另一个古怪之处是
model.name = $event
。 之前看到的$event
对象来自 DOM 事件。 但ngModelChange
属性不会生成 DOM 事件 —— 它是AngularEventEmitter
类型的属性,当它触发时, 它返回的是输入框的值 —— 也正是希望赋给模型name
属性的值。 - 在表单中使用
ngModel
可以获得比仅使用双向数据绑定更多的控制权。它还会告诉我们很多信息:用户碰过此控件吗?它的值变化了吗?数据变得无效了吗? - NgModel 指令不仅仅跟踪状态。它还使用特定的 Angular CSS 类来更新控件,以反映当前状态。 可以利用这些 CSS 类来修改控件的外观,显示或隐藏消息。
状态@H_400_301@ | 为真时的CSS类@H_400_301@ | 为假时的CSS类@H_400_301@ |
---|---|---|
控件已经被访问过 | ng-touched |
ng-untouched |
控件值已经变化 | ng-dirty |
ng-pristine |
控件值是有效的 | ng-valid |
ng-invalid |
13. 在控件上加入模板引用变量,通过模板引用变量.className
可以查看当前控件所拥有的类。
14. 模板引用变量可以访问模板中输入框的 Angular 控件。如:
<input type="text" required [(ngModel)]="model.name" name="name" #name="ngModel">
这里,创建了名为name的变量,并且赋值为“ngModel”。
注:为什么是“ngModel”?指令exportAs属性告诉Angular如何链接模板引用变量到指令。这里把name
设置为ngModel
是因为ngModel
指令的exportAs
属性设置成了“ngModel”。
15. 在form表单上添加模板引用变量,并且赋值为ngForm
,如:#heroForm="ngForm"
,现在heroForm变量引用的是NgForm指令,它代表的是表单的整体。可以在Angular表达式中使用该模板引用变量的reset方法,重置表单的状态。如:
<button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button>
.
16. 什么NgForm
指令?之前没有添加过 NgForm 指令啊!是 Angular 干的。Angular 自动创建了NgForm指令,并把它附加到标签。
17. NgForm
指令为form元素扩充了额外的特性。它持有通过ngModel指令和name属性为各个元素创建的那些控件,并且监视它们的属性变化,包括有效性。它还有自己的valid
属性,只有当其中所有控件都有效时,它才有效。
18. 如可以通过heroForm变量(上述中提到的模板变量heroForm)把按钮的disabled属性绑定到表单的整体有效性。
<button type="submit" [disabled]="!heroForm.form.valid">Submit</button>
案例angular-forms
模型类 hero.ts
export class Hero {
constructor(public id: number,public name: string,public power: string,public alterEgo?: string) {
}
}
组件 hero-form.component.ts
import { Component } from '@angular/core';
import { Hero } from './hero';
@Component({
moduleId: module.id,selector: 'hero-form',templateUrl: 'hero-form.component.html'
})
export class HeroFormComponent {
powers = [
'Really smart','Super Flexible','Super Hot','Weather Changer'
];
model = new Hero(18,'Dr IQ',this.powers[0],'Chuck Overstreet');
submitted = false;
onSubmit() {
this.submitted = true;
}
// TODO: Remove this when we're done
get diagnostic() {
return JSON.stringify(this.model);
}
newHero() {
this.model = new Hero(42,'','');
}
}
hero-form.component.html
<div class="container"> <div [hidden]="submitted"> <h1>Hero Form</h1> <form (ngSubmit)="onSubmit()" #heroForm="ngForm"> <!--{{diagnostic}}--> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" id="name" required [(ngModel)]="model.name" name="name" #name="ngModel"> </div> <div [hidden]="name.valid || name.pristine" class="alert alert-danger"> Name is required </div> <div class="form-group"> <label for="alterEgo">Alter Ego</label> <input type="text" class="form-control" id="alterEgo" [(ngModel)]="model.alterEgo" name="alterEgo"> </div> <div class="form-group"> <label for="power">Hero Power</label> <select id="power" class="form-control" required [(ngModel)]="model.power" name="power"> <option *ngFor="let pow of powers" [value]="pow">{{pow}}</option> </select> </div> <button type="submit" class="btn btn-default" [disabled]="!heroForm.form.valid">Submit</button> </form> <br/> <button type="button" class="btn btn-default" (click)="newHero(); heroForm.reset()">New Hero</button> </div> <div [hidden]="!submitted"> <h2>You submitted the following:</h2> <div class="row"> <div class="col-xs-3">Name</div> <div class="col-xs-9 pull-left">{{ model.name }}</div> </div> <div class="row"> <div class="col-xs-3">Alter Ego</div> <div class="col-xs-9 pull-left">{{ model.alterEgo }}</div> </div> <div class="row"> <div class="col-xs-3">Power</div> <div class="col-xs-9 pull-left">{{ model.power }}</div> </div> <br> <button class="btn btn-default" (click)="submitted=false">Edit</button> </div> </div>
根模块 app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeroFormComponent } from './hero-form.component';
@NgModule({
imports: [ BrowserModule,FormsModule ],declarations: [ AppComponent,HeroFormComponent ],bootstrap: [ AppComponent ]
})
export class AppModule { }
注:因为模板驱动的表单位于它们自己的模块,所以在使用表单之前,需要将FormsModule
添加到应用模块的imports
数组中。
根组件 app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',template: `<hero-form></hero-form>`,})
export class AppComponent { }
引导根模块 main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
示例页面 index.html
<my-app>Loading AppComponent content here ...</my-app>
定制CSS类,提供无效控件的视觉反馈 forms.css
.ng-valid[required],.ng-valid.required { border-left: 5px solid #42A948; /* green */ }
.ng-invalid:not(form) { border-left: 5px solid #a94442; /* red */ }