组件与模板(二) 模板语法

时间:2021-05-14 05:28:52

  Angular 应用管理着用户之所见和所为,并通过 Component 类的实例(组件)和面向用户的模板来与用户交互。

  从使用模型-视图-控制器 (MVC) 或模型-视图-视图模型 (MVVM) 的经验中,很多开发人员都熟悉了组件和模板这两个概念。 在 Angular 中,组件扮演着控制器或视图模型的角色,模板则扮演视图的角色。

  模板绑定是通过 (DOM) property 和事件来工作的,而不是 (HTML) attribute。

  在 Angular 的世界中,attribute 唯一的作用是用来初始化元素和指令的状态。 当进行数据绑定时,只是在与元素和指令的 property 和事件打交道,而 attribute 就完全靠边站了。

绑定语法:概览

绑定的类型可以根据数据流的方向分成三类: 从数据源到视图、从视图到数据源以及双向的从视图到数据源再到视图。

数据方向

DOM 属性(Property)语法

绑定类型

单向
从数据源
到视图

{{expression}}

[target]="expression"

bind-target="expression"

插值表达式
属性
Attribute
CSS 类
样式

从视图到数据源的单向绑定

(target)="statement"

on-target="statement"

事件

双向

[(target)]="expression"

bindon-target="expression"

双向

 

绑定目标

数据绑定的目标是 DOM 中的某些东西。 这个目标可能是(元素 | 组件 | 指令的)property、(元素 | 组件 | 指令的)事件,或(极少数情况下) attribute 名。 下面是的汇总表:

绑定类型

目标

范例

属性

元素的 property
组件的 property
指令的 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 类

class property

<div [class.special]="isSpecial">Special</div>

样式

style property

<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)。

内置指令

可分为属性型指令 或 结构型指令

最常用的属性型指令:

使用[(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 的输出属性来监听元素值的变化。

 

内置结构型指令

常见结构型指令的简介:

  • NgIf - 根据条件把一个元素添加到 DOM 中或从 DOM 移除

  • NgSwitch 一组指令,用来在多个可选视图之间切换。

  • NgForOf - 对列表中的每个条目重复套用同一个模板