用户的操作,如点击链接,按下按钮,输入文字引发DOM事件。 本页说明如何使用Angular事件绑定语法将这些事件绑定到组件事件处理程序。
您可以使用Angular事件绑定来响应任何DOM事件。 许多DOM事件由用户输入触发。 绑定到这些事件提供了从用户获得输入的方法。
要绑定到DOM事件,请在括号中包围DOM事件名称,并为其分配引用的模板语句。 以下示例显示了实现click处理程序的事件绑定:
<button (click)="onClickMe()">Click me!</button>
等号左边的(click)标识按钮的点击事件作为绑定的目标。 等号右边的引号中的文本是模板语句,它通过调用组件的onClickMe()方法来响应click事件。
在编写绑定时,请注意模板语句的执行上下文。 模板语句中的标识符属于特定的上下文对象,通常是控制模板的Angular组件。 上面的例子显示了一行HTML,但是HTML属于一个更大的组件:
lib/src/click_me_component.dart (component)
@Component(
selector: 'click-me',
template: '''
<button (click)="onClickMe()">Click me!</button>
{{clickMessage}}
''',
)
class ClickMeComponent {
String clickMessage = '';
void onClickMe() => clickMessage = 'You are my hero!';
}
当用户点击按钮时,Angular从ClickMeComponent调用onClickMe()方法。
DOM事件携带可能对组件有用的信息的有效载荷。 本节介绍如何绑定到输入框的按键事件,以在每次按键后获取用户的输入。
下面的代码监听一个keyup事件,并将整个事件有效载荷($ event)传递给组件事件处理程序。
lib/src/keyup_components.dart (v1 template)
template: '''
<input (keyup)="onKey(\$event)">
<p>{{values}}</p>
''',
$EVENT VS. \$EVENT Dart文件中的非原始字符串需要$前面的\。 如果模板位于HTML文件中,请使用$ event而不是\ $event。
当用户按下并释放一个键时,会发生一个键盘事件,而Angular在$ event变量中提供一个相应的DOM事件对象,该代码将该代码作为参数传递给组件的onKey()方法。
lib/src/keyup_components.dart (v1 class, untyped)
class KeyUp1Component {
String values = '';
void onKey(dynamic event) {
values += event.target.value + ' | ';
}
}
$ event对象的属性取决于DOM事件的类型。 例如,鼠标事件包含与输入框编辑事件不同的信息。
所有标准的DOM Event对象都有一个target属性,它是引发事件的元素的引用。 在这种情况下,target指向<input>元素,event.target.value返回该元素的当前内容。
每次调用之后,onKey()方法将输入框值附加到组件的values属性,后跟一个分隔符(|)。 该模板使用Angular插值({{...}})来显示值属性。
假设用户输入字母“abc”,然后退格逐一删除。 以下是UI显示的内容:
a | ab | abc | ab | a | |
或者,您可以通过用event.key替换event.target.value来累积每个Key。 在这种情况下,相同的用户输入会产生以下结果:
a | b | c | Backspace | Backspace | Backspace |
上面的例子声明了onKey()事件参数是动态的。 虽然这简化了一些代码,但使用更具体的类型可以揭示事件对象的属性并防止愚蠢的错误。
以下示例用类型重写该方法:lib/src/keyup_components.dart (v1 class)
class KeyUp1Component {
String values = '';
void onKey(KeyboardEvent event) {
InputElement el = event.target;
values += '${el.value} | ';
}
}
现在,事件被声明为KeyboardEvent,event.target作为InputElement - 具有value属性的元素类型之一。 有了这些类型,onKey()方法就可以更清楚地表达它对模板的期望,以及它如何解释事件。
键入事件对象揭示了将整个DOM事件传递到方法中的一个重要问题:组件与模板细节密切相关。 如果不使用Web API,组件将无法提取数据。 这打破了模板(用户看到的)和组件(应用程序如何处理用户数据)之间的关系分离。
下一节将介绍如何使用模板引用变量来解决这个问题。
还有另一种获取用户数据的方法:Angular 模板引用变量提供了对模板内的元素的直接访问。 要声明模板引用变量,请在标识符前加一个哈希字符(#)。
以下示例使用模板引用变量在简单模板中实现按键回送。
lib/src/loop_back_component.dart
@Component(
selector: 'loop-back',
template: '''
<input #box (keyup)="0">
<p>{{box.value}}</p>
''',
)
class LoopBackComponent {}
名为box的模板引用变量在<input>元素上声明,引用<input>元素本身。 代码使用box变量来获取输入元素的值,并在<p>标签之间进行插值显示。 模板是完全独立的。 它不绑定到组件,组件什么也不做。 在输入框中输入内容,然后观看每个按键显示更新。
除非你绑定一个事件,否则这根本不起作用。 Angular仅在应用程序响应异步事件(如击键)时才更新绑定(以及屏幕)。 这个例子绑定了keyup事件到数字0,尽可能最短的模板语句。 虽然该声明没有任何用处,但符合Angular的要求,所以Angular将更新屏幕。
使用模板引用变量到达输入框比通过$ event对象更容易。 这里是重写前一个使用模板引用变量来获取用户输入的关键示例。lib/src/keyup_components.dart (v2)
@Component(
selector: 'key-up2',
template: '''
<input #box (keyup)="onKey(box.value)">
<p>{{values}}</p>
''',
)
class KeyUp2Component {
String values = '';
void onKey(value) => values += '$value | ';
}
这种方法的一个很好的方面是该组件从视图中获取干净的数据值。 它不再需要了解$event及其结构的知识。
(keyup)事件处理程序听到每个击键。 有时只有Enter键很重要,因为它表示用户已经完成打字。 减少噪音的一种方法是检查每个$ event.keyCode,并且只有当输入键是enter时才采取行动。
有一个更简单的方法:绑定到Angular的keyup.enter伪事件。 然后,只有当用户按下Enter时,Angular才会调用事件处理程序。
lib/src/keyup_components.dart (v3)
@Component(
selector: 'key-up3',
template: '''
<input #box (keyup.enter)="values=box.value">
<p>{{values}}</p>
''',
)
class KeyUp3Component {
String values = '';
}
这是如何工作的。
在前面的示例中,如果用户在没有首先按下Enter的情况下单击页面上的其他位置,则输入框的当前状态将丢失。 只有当用户按下Enter时,组件的value属性才会更新。
要解决此问题,请同时听取Enter键和blur事件。
lib/src/keyup_components.dart (v4)
@Component(
selector: 'key-up4',
template: '''
<input #box
(keyup.enter)="values=box.value"
(blur)="values=box.value">
<p>{{values}}</p>
''',
)
class KeyUp4Component {
String values = '';
}
上一页显示了如何显示数据。 本页展示了事件绑定技术。
现在,把它放在一个微型应用程序,可以显示英雄列表,并添加新的英雄列表。 用户可以通过在输入框中输入英雄的名字并点击添加来添加英雄。
下面是“英雄之旅”组件。 lib / src / little_tour_component.dart(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="let hero of heroes">{{hero}}</li></ul>
''',
directives: const [CORE_DIRECTIVES],
)
class LittleTourComponent {
List<String> heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
void addHero(String newHero) {
if (newHero?.length > 0) {
heroes.add(newHero);
}
}
}
使用模板变量来引用元素。 newHero模板变量引用<input>元素。 您可以从<input>元素的任何兄弟或子元素引用newHero。
传递值,而不是元素。 取而代之的是将newHero传递给组件的addHero()方法,获取输入框的值并将其传递给addHero()。
保持模板语句简单。 (blur)事件绑定到两个语句。 第一个语句调用addHero()。 第二个语句newHero.value =''在新的英雄添加到列表后清除输入框。
这里是在这个页面中讨论的所有代码。
lib/src/click_me_component.dart
import 'package:angular/angular.dart';
@Component(
selector: 'click-me',
template: '''
<button (click)="onClickMe()">Click me!</button>
{{clickMessage}}
''',
)
class ClickMeComponent {
String clickMessage = '';
void onClickMe() => clickMessage = 'You are my hero!';
}
lib/src/keyup_components.dart
import 'dart:html';
import 'package:angular/angular.dart';
@Component(
selector: 'key-up1-untyped',
template: '''
<input (keyup)="onKey(\$event)">
<p>{{values}}</p>
''',
)
class KeyUp1Component_untyped {
String values = '';
void onKey(dynamic event) {
values += event.target.value + ' | ';
}
}
@Component(
selector: 'key-up1',
template: '''
<input (keyup)="onKey(\$event)">
<p>{{values}}</p>
''',
)
class KeyUp1Component {
String values = '';
void onKey(KeyboardEvent event) {
InputElement el = event.target;
values += '${el.value} | ';
}
}
@Component(
selector: 'key-up2',
template: '''
<input #box (keyup)="onKey(box.value)">
<p>{{values}}</p>
''',
)
class KeyUp2Component {
String values = '';
void onKey(value) => values += '$value | ';
}
@Component(
selector: 'key-up3',
template: '''
<input #box (keyup.enter)="values=box.value">
<p>{{values}}</p>
''',
)
class KeyUp3Component {
String values = '';
}
@Component(
selector: 'key-up4',
template: '''
<input #box
(keyup.enter)="values=box.value"
(blur)="values=box.value">
<p>{{values}}</p>
''',
)
class KeyUp4Component {
String values = '';
}
lib/src/loop_back_component.dart
import 'package:angular/angular.dart';
@Component(
selector: 'loop-back',
template: '''
<input #box (keyup)="0">
<p>{{box.value}}</p>
''',
)
class LoopBackComponent {}
lib/src/little_tour_component.dart
import 'package:angular/angular.dart';
@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="let hero of heroes">{{hero}}</li></ul>
''',
directives: const [CORE_DIRECTIVES],
)
class LittleTourComponent {
List<String> heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
void addHero(String newHero) {
if (newHero?.length > 0) {
heroes.add(newHero);
}
}
}
您已经看到了用于响应用户输入和手势的基本原语。
这些技术对于小型演示很有用,但是在处理大量的用户输入时会很快变得冗长和笨拙。 双向数据绑定是在数据输入字段和模型属性之间移动值的更优雅和紧凑的方式。 下一页,Forms介绍了如何使用NgModel编写双向绑定。