在线文档教程

用户输入

用户输入

当用户点击链接、按下按钮或者输入文字时,这些用户动作都会产生 DOM 事件。 本章解释如何使用 Angular 事件绑定语法把这些事件绑定到事件处理器。

运行在线例子 / 下载范例

绑定到用户输入事件

你可以使用 Angular 事件绑定机制来响应任何 DOM 事件。 许多 DOM 事件是由用户输入触发的。绑定这些事件可以获取用户输入。

要绑定 DOM 事件,只要把 DOM 事件的名字包裹在圆括号中,然后用放在引号中的模板语句对它赋值就可以了。

下例展示了一个事件绑定,它实现了一个点击事件处理器:

src/app/click-me.component.ts

content_copy<button (click)="onClickMe()">Click me!</button>

等号左边的 (click) 表示把按钮的点击事件作为绑定目标。 等号右边引号中的文本是模板语句,通过调用组件的 onClickMe 方法来响应这个点击事件。

写绑定时,需要知道模板语句的执行上下文。 出现在模板语句中的每个标识符都属于特定的上下文对象。 这个对象通常都是控制此模板的 Angular 组件。 上例中只显示了一行 HTML,那段 HTML 片段属于下面这个组件:

src/app/click-me.component.ts

content_copy@Component{ selector: 'app-click-me', template: ` <button (click)="onClickMe()">Click me!</button> {{clickMessage}}` }) export class ClickMeComponent { clickMessage = ''; onClickMe() { this.clickMessage = 'You are my hero!'; } }

当用户点击按钮时,Angular 调用 ClickMeComponentonClickMe 方法。

通过 $event 对象取得用户输入

DOM 事件可以携带可能对组件有用的信息。 本节将展示如何绑定输入框的 keyup 事件,在每个敲击键盘时获取用户输入。

下面的代码监听 keyup 事件,并将整个事件载荷 ($event) 传递给组件的事件处理器。

src/app/keyup.components.ts (template v.1)

content_copytemplate: ` <input (keyup)="onKey($event)"> <p>{{values}}</p> `

当用户按下并释放一个按键时,触发 keyup 事件,Angular 在 $event 变量提供一个相应的 DOM 事件对象,上面的代码将它作为参数传递给 onKey() 方法。

src/app/keyup.components.ts (class v.1)

content_copyexport class KeyUpComponent_v1 { values = ''; onKey(event: any) { // without type info this.values += event.target.value + ' | '; } }

$event 对象的属性取决于 DOM 事件的类型。例如,鼠标事件与输入框编辑事件包含了不同的信息。

所有标准 DOM 事件对象都有一个 target 属性, 引用触发该事件的元素。 在本例中,target 是<input> 元素, event.target.value 返回该元素的当前内容。

在组件的 onKey() 方法中,把输入框的值和分隔符 (|) 追加组件的 values属性。 使用插值表达式来把存放累加结果的 values 属性回显到屏幕上。

假设用户输入字母“abc”,然后用退格键一个一个删除它们。 用户界面将显示:

content_copya | ab | abc | ab | a | |

或者,你可以用 event.key 替代 event.target.value,积累各个按键本身,这样同样的用户输入可以产生:

content_copya | b | c | backspace | backspace | backspace |

$event的类型

上例将 $event 转换为 any 类型。 这样简化了代码,但是有成本。 没有任何类型信息能够揭示事件对象的属性,防止简单的错误。

下面的例子,使用了带类型方法:

src/app/keyup.components.ts (class v.1 - typed )

content_copyexport class KeyUpComponent_v1 { values = ''; onKey(event: KeyboardEvent) { // with type info this.values += (<HTMLInputElement>event.target).value + ' | '; } }

$event 的类型现在是 KeyboardEvent。 不是所有的元素都有 value 属性,所以它将 target 转换为输入元素。 OnKey 方法更加清晰地表达了它期望从模板得到什么,以及它是如何解析事件的。

传入 $event 是靠不住的做法

类型化事件对象揭露了重要的一点,即反对把整个 DOM 事件传到方法中,因为这样组件会知道太多模板的信息。 只有当它知道更多它本不应了解的 HTML 实现细节时,它才能提取信息。 这就违反了模板(用户看到的)和组件(应用如何处理用户数据)之间的分离关注原则。

下面将介绍如何用模板引用变量来解决这个问题。

从一个模板引用变量中获得用户输入

还有另一种获取用户数据的方式:使用 Angular 的模板引用变量。 这些变量提供了从模块中直接访问元素的能力。 在标识符前加上井号 (#) 就能声明一个模板引用变量

下面的例子使用了局部模板变量,在一个超简单的模板中实现按键反馈功能。

src/app/loop-back.component.ts

content_copy@Component{ selector: 'app-loop-back', template: ` <input #box (keyup)="0"> <p>{{box.value}}</p> ` }) export class LoopbackComponent { }

这个模板引用变量名叫 box,在 <input> 元素声明,它引用 <input> 元素本身。 代码使用 box 获得输入元素的 value 值,并通过插值表达式把它显示在 <p> 标签中。

这个模板完全是完全自包含的。它没有绑定到组件,组件也没做任何事情。

在输入框中输入,就会看到每次按键时,显示也随之更新了。

除非你绑定一个事件,否则这将完全无法工作。

只有在应用做了些异步事件(如击键),Angular 才更新绑定(并最终影响到屏幕)。 本例代码将 keyup 事件绑定到了数字 0,这可能是最短的模板语句了。 虽然这个语句不做什么,但它满足 Angular 的要求,所以 Angular 将更新屏幕。

从模板变量获得输入框比通过 $event 对象更加简单。 下面的代码重写了之前 keyup 示例,它使用变量来获得用户输入。

src/app/keyup.components.ts (v2)

content_copy@Component{ selector: 'app-key-up2', template: ` <input #box (keyup)="onKey(box.value)"> <p>{{values}}</p> ` }) export class KeyUpComponent_v2 { values = ''; onKey(value: string) { this.values += value + ' | '; } }

这个方法最漂亮的一点是:组件代码从视图中获得了干净的数据值。再也不用了解 $event 变量及其结构了。

按键事件过滤(通过 key.enter)

(keyup) 事件处理器监听每一次按键。 有时只在意回车键,因为它标志着用户结束输入。 解决这个问题的一种方法是检查每个 $event.keyCode,只有键值是回车键时才采取行动。

更简单的方法是:绑定到 Angular 的 keyup.enter 模拟事件。 然后,只有当用户敲回车键时,Angular 才会调用事件处理器。

src/app/keyup.components.ts (v3)

content_copy@Component{ selector: 'app-key-up3', template: ` <input #box (keyup.enter)="onEnter(box.value)"> <p>{{value}}</p> ` }) export class KeyUpComponent_v3 { value = ''; onEnter(value: string) { this.value = value; } }

下面展示了它是如何工作的。

失去焦点事件 (blur)

前上例中,如果用户没有先按回车键,而是移开了鼠标,点击了页面中其它地方,输入框的当前值就会丢失。 只有当用户按下了回车键候,组件的 values 属性才能更新。

下面通过同时监听输入框的回车键和失去焦点事件来修正这个问题。

src/app/keyup.components.ts (v4)

content_copy@Component{ selector: 'app-key-up4', template: ` <input #box (keyup.enter)="update(box.value)" (blur)="update(box.value)"> <p>{{value}}</p> ` }) export class KeyUpComponent_v4 { value = ''; update(value: string) { this.value = value; } }

把它们放在一起

上一章介绍了如何显示数据。 本章展示了事件绑定技术。

现在,在一个微型应用中一起使用它们,应用能显示一个英雄列表,并把新的英雄加到列表中。 用户可以通过输入英雄名和点击“添加”按钮来添加英雄。

下面就是“简版英雄指南”组件。

src/app/little-tour.component.ts

content_copy@Component{ selector: 'app-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="let hero of heroes">{{hero}}</li></ul> ` }) export class LittleTourComponent { heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado']; addHero(newHero: string) { if (newHero) { this.heroes.push(newHero } } }

小结

  • 使用模板变量来引用元素 — newHero 模板变量引用了 <input> 元素。 你可以在 <input> 的任何兄弟或子级元素中引用 newHero。

源代码

下面是本章讨论过的所有源码。

click-me.component.ts

keyup.components.ts

loop-back.component.ts

little-tour.component.ts

content_copyimport { Component } from '@angular/core'; @Component{ selector: 'app-click-me', template: ` <button (click)="onClickMe()">Click me!</button> {{clickMessage}}`})export class ClickMeComponent { clickMessage = '';  onClickMe() { this.clickMessage = 'You are my hero!'; }}

小结

你已经掌握了响应用户输入和操作的基础技术。

这些技术对小规模演示很实用,但是在处理大量用户输入时,很容易变得累赘和笨拙。 要在数据录入字段和模型属性之间传递数据,双向数据绑定是更加优雅和简洁的方式。 下一章 表单 解释了如何用 NgModel 来进行双向绑定。