Angular 应用管理着用户之所见和所为,并通过 Component 类的实例(组件)和面向用户的模板来与用户交互。
从使用模型-视图-控制器 (MVC) 或模型-视图-视图模型 (MVVM) 的经验中,很多开发人员都熟悉了组件和模板这两个概念。 在 Angular 中,组件扮演着控制器或视图模型的角色,模板则扮演视图的角色。
模板绑定是通过 (DOM) property 和事件来工作的,而不是 (HTML) attribute。
在 Angular 的世界中,attribute 唯一的作用是用来初始化元素和指令的状态。 当进行数据绑定时,只是在与元素和指令的 property 和事件打交道,而 attribute 就完全靠边站了。
绑定语法:概览
绑定的类型可以根据数据流的方向分成三类: 从数据源到视图、从视图到数据源以及双向的从视图到数据源再到视图。
数据方向 |
DOM 属性(Property)语法 |
绑定类型 |
---|---|---|
单向 |
{{expression}} [target]="expression" bind-target="expression" |
插值表达式 |
从视图到数据源的单向绑定 |
(target)="statement" on-target="statement" |
事件 |
双向 |
[(target)]="expression" bindon-target="expression" |
双向 |
绑定目标
数据绑定的目标是 DOM 中的某些东西。 这个目标可能是(元素 | 组件 | 指令的)property、(元素 | 组件 | 指令的)事件,或(极少数情况下) attribute 名。 下面是的汇总表:
绑定类型 |
目标 |
范例 |
---|---|---|
属性 |
元素的 property |
//元素属性设置为组件属性的值 <img [src]="heroImageUrl"> //设置自定义组件的模型属性(这是父子组件之间通讯的重要途径): <app-hero-detail [hero]="currentHero"></app-hero-detail> //指令的属性 <div [ngClass]="{'special': isSpecial}"></div> |
事件 |
元素的事件 |
<button (click)="onSave()">Save</button> <app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail> <div (myClick)="clicked=$event" clickable>click me</div> |
双向 |
事件与 property |
<input [(ngModel)]="name"> |
Attribute |
attribute(例外情况) |
<button [attr.aria-label]="help">help</button> |
CSS 类 |
|
<div [class.special]="isSpecial">Special</div> |
样式 |
|
<button [style.color]="isSpecial ? 'red' : 'green'"> |
事件名和目标事件
圆括号中的名称 —— 比如 (click)
—— 标记出目标事件。在下面例子中,目标是按钮的 click 事件。
<button (click)="onSave()">Save</button>
有些人更喜欢带 on-
前缀的备选形式,称之为规范形式:
<button on-click="onSave()">On Save</button>
元素事件可能是更常见的目标,但 Angular 会先看这个名字是否能匹配上已知指令的事件属性,就像下面这个例子:
<!-- `myClick` is an event on the custom `ClickDirective` --> <div (myClick)="clickMessage=$event" clickable>click with myClick</div>
$event 和事件处理语句
在事件绑定中,Angular 会为目标事件设置事件处理器。
当事件发生时,这个处理器会执行模板语句。 典型的模板语句通常涉及到响应事件执行动作的接收器,例如从 HTML 控件中取得值,并存入模型。
绑定会通过名叫 $event
的事件对象传递关于此事件的信息(包括数据值)。
事件对象的形态取决于目标事件。如果目标事件是原生 DOM 元素事件, $event
就是 DOM 事件对象,它有像 target
和 target.value
这样的属性。
考虑这个范例:
<input [value]="currentHero.name" (input)="currentHero.name=$event.target.value" >
上面的代码在把输入框的 value
属性绑定到 name
属性。 要监听对值的修改,代码绑定到输入框的 input
事件。 当用户造成更改时,input
事件被触发,并在包含了 DOM 事件对象 ($event
) 的上下文中执行这条语句。
要更新 name
属性,就要通过路径 $event.target.value
来获取更改后的值。
使用 EventEmitter 实现自定义事件
通常,指令使用 Angular EventEmitter 来触发自定义事件。 指令创建一个 EventEmitter
实例,并且把它作为属性暴露出来。 指令调用 EventEmitter.emit(payload)
来触发事件,可以传入任何东西作为消息载荷。 父指令通过绑定到这个属性来监听事件,并通过 $event
对象来访问载荷。
假设 HeroDetailComponent
用于显示英雄的信息,并响应用户的动作。 虽然 HeroDetailComponent
包含删除按钮,但它自己并不知道该如何删除这个英雄。 最好的做法是触发事件来报告“删除用户”的请求。
下面的代码节选自 HeroDetailComponent
:
template: ` <div> <img src="{{heroImageUrl}}"> <span [style.text-decoration]="lineThrough"> {{prefix}} {{hero?.name}} </span> <button (click)="delete()">Delete</button> </div>`
// This component makes a request but it can't actually delete a hero. deleteRequest = new EventEmitter<Hero>(); delete() { this.deleteRequest.emit(this.hero); }
组件定义了 deleteRequest
属性,它是 EventEmitter
实例。 当用户点击删除时,组件会调用 delete()
方法,让 EventEmitter
发出一个 Hero
对象。
现在,假设有个宿主的父组件,它绑定了 HeroDetailComponent
的 deleteRequest
事件。
<app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-hero-detail>
当 deleteRequest
事件触发时,Angular 调用父组件的 deleteHero
方法, 在 $event
变量中传入要删除的英雄(来自 HeroDetail
)。
内置指令
最常用的属性型指令:
-
NgClass
- 添加或移除一组 CSS 类 -
NgStyle
- 添加或移除一组 CSS 样式 -
NgModel
- 双向绑定到 HTML 表单元素 :使用ngModel
时需要FormsModule
使用[(ngModel)]双向绑定到表单元素
导入 FormsModule
并让 [(ngModel)]
可用的代码如下:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; // JavaScript import from Angular /* Other imports */ @NgModule({ imports: [ BrowserModule, FormsModule // import into the NgModule ], /* Other module metadata */ }) export class AppModule { }
[(ngModel)]内幕
回头看看 name
绑定,注意,你可以通过分别绑定到 <input>
元素的 value
属性和 input
事件来达到同样的效果。
<input [value]="currentHero.name" (input)="currentHero.name=$event.target.value" >
那样显得很笨重,谁会记得该设置哪个元素属性以及当用户修改时触发哪个事件? 你该如何提取输入框中的文本并且更新数据属性?谁会希望每次都去查资料来确定这些?
ngModel
指令通过自己的输入属性 ngModel
和输出属性 ngModelChange
隐藏了那些细节。
<input [ngModel]="currentHero.name" (ngModelChange)="currentHero.name=$event">
ngModel
输入属性会设置该元素的值,并通过 ngModelChange
的输出属性来监听元素值的变化。
内置结构型指令
常见结构型指令的简介: