Angular4-学习笔记-4-模板语法

学习资料来自 Angular.cn 与 Angular.io。

模板语法

在线例子

在 Angular 中,组件扮演着控制器或视图模型的角色,模板则扮演视图的角色。

模板中的 HTML

HTML 是 Angular 模板的语言。

Hello Angular

为防范脚本注入攻击的风险,Syntax';

"{{evilTitle}}" is the interpolated evil title.

"" is the property bound evil title.

attribute、class 和 style 绑定

attribute 绑定

语法:[attr.attribute-name]

可以通过 attribute 绑定来直接设置 attribute 的值,因为当元素没有属性可绑的时候,就必须使用 attribute 绑定。

Three-Four

该语句会报错如下:

Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
(模板解析错误:不能绑定到 'colspan',因为它不是已知的原生属性)

正确的写法:

One-Two
FiveSix

attribute 绑定的主要用例之一是设置 ARIA attribute

ARIA 指可访问性,用于给残障人士访问互联网提供便利。



CSS 类绑定

语法:[class.class-name]


Bad curly special
Bad curly
The class binding is special
This one is not so special

通常更喜欢使用 ngClass 指令来同时管理多个类名。

样式绑定

语法:[style.style-property]



带有单位的绑定:



通常更喜欢使用 ngStyle 指令来同时设置多个内联样式。

事件绑定 (Event binding) (event)

语法:(目标事件)="模版语句"
(target event)="template statement"


目标事件

元素事件可能是更常见的目标,但 Angular 会先看这个名字是否能匹配上已知指令的事件属性,如:


click with myClick

如果这个名字没能匹配到元素事件或已知指令的输出属性,Angular 就会报“未知指令”错误。

$event 和事件处理语句(event handling statements)

在事件绑定中,Angular 会为目标事件设置事件处理器。当事件发生时,这个处理器会执行模板语句。典型的模板语句通常涉及到响应事件执行动作的接收器,例如从 HTML 控件中取得值,并存入模型。
In an event binding, Angular sets up an event handler for the target event. When the event is raised, the handler executes the template statement. The template statement typically involves a receiver, which performs an action in response to the event, such as storing a value from the HTML control into a model.

绑定会通过名叫 $event 的事件对象传递关于此事件的信息(包括数据值)。

事件对象的形态取决于目标事件。
The shape of the event object is determined by the target event.

如果目标事件是原生 DOM 元素事件, $event 就是 DOM事件对象,它有像 targettarget.value 这样的属性。


如果事件属于指令,那么 $event 具体是什么由指令决定。

使用 EventEmitter 实现自定义事件

通常,指令使用 EventEmitter 来触发自定义事件。指令创建一个 EventEmitter 实例,并且把它作为属性暴露出来。指令调用 EventEmitter.emit(payload) 来触发事件,可以传入任何东西作为消息载荷。 父指令通过绑定到这个属性来监听事件,并通过 $event 对象来访问载荷。

示例

假设 HeroDetailComponent 用于显示英雄的信息,并响应用户的动作。 虽然 HeroDetailComponent 包含删除按钮,但它自己并不知道该如何删除这个英雄。 最好的做法是触发事件来报告“删除用户”的请求。

src/app/hero-detail.component.ts (template)

template: `
![]({{heroImageUrl}}) {{prefix}} {{hero?.name}}
`

src/app/hero-detail.component.ts (deleteRequest)

// This component make a request but it can't actually delete a hero.
deleteRequest = new EventEmitter();

delete() {
  this.deleteRequest.emit(this.hero);
}

说明:组件定义了 deleteRequest 属性,它是 EventEmitter 实例。 当用户点击删除时,组件会调用 delete() 方法,让 EventEmitter 发出一个 Hero 对象。

现在,假设有个宿主的父组件,它绑定了 HeroDetailComponentdeleteRequest 事件。


deleteRequest 事件触发时,Angular 调用父组件的 deleteHero 方法, 在 $event 变量中传入要删除的英雄(来自 HeroDetail)。

模板语句有副作用

模板语句的副作用不仅没问题,反而正是所期望的。

双向数据绑定 (Two-way binding) [(...)]

语法:[(x)]

[(x)] 语法结合了属性绑定的方括号 [x] 和事件绑定的圆括号 (x)。当一个元素拥有可以设置的属性 x 和对应的事件 xChange 时,就可以使用 [(x)] 语法 。

示例 src/app/sizer.component.ts

import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
  selector: 'my-sizer',
  template: `
  
` }) export class SizerComponent { @Input() size: number | string; @Output() sizeChange = new EventEmitter(); dec() { this.resize(-1); } inc() { this.resize(+1); } resize(delta: number) { this.size = Math.min(40, Math.max(8, +this.size + delta)); this.sizeChange.emit(this.size); } }

AppComponent.fontSize 被双向绑定到 SizerComponent


Resizable Text

Angular 将 SizerComponent 的绑定分解成这样:


说明:$event 变量包含了 SizerComponent.sizeChange 事件的荷载。 当用户点击按钮时,Angular 将 $event 赋值给 AppComponent.fontSizePx

内置指令

内置属性型指令(Built-in attribute directives)

属性型指令会监听和修改其它 HTML 元素或组件的行为、元素 Attribute、DOM Property。 它们通常会作为 HTML Attribute 的名称而应用在元素上。

常见的内置属性型指令:

  • NgClass 添加或移除一组CSS类
  • NgStyle 添加或移除一组CSS样式
  • NgModel 双向绑定到 HTML 表单元素

NgClass

示例:组件方法 setCurrentClasses 可以把组件的属性 currentClasses 设置为一个对象,它将会根据三个其它组件的状态为 truefalse 而添加或移除三个类。

currentClasses: {};
setCurrentClasses() {
  // CSS classes: added/removed per current state of component properties
  this.currentClasses =  {
    saveable: this.canSave,
    modified: !this.isUnchanged,
    special:  this.isSpecial
  };
}

ngClass 属性绑定到 currentClasses,根据它来设置此元素的 CSS 类:

This div is initially saveable, unchanged, and special

你既可以在初始化时调用 setCurrentClassess(),也可以在所依赖的属性变化时调用。

NgStyle

ngStyle 需要绑定到一个 key:value 控制对象。 对象的每个 key 是样式名,它的 value 是能用于这个样式的任何值。

currentStyles: {};
setCurrentStyles() {
  // CSS styles: set per current state of component properties
  this.currentStyles = {
    'font-style':  this.canSave      ? 'italic' : 'normal',
    'font-weight': !this.isUnchanged ? 'bold'   : 'normal',
    'font-size':   this.isSpecial    ? '24px'   : '12px'
  };
}

This div is initially italic, normal weight, and extra large (24px).

NgModel

要使用 ngModel 需要导入 FormsModule 模块。

示例:

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 { }

更多关于 FormsModulengModel 的内容参见表单。

使用 ngModel 实现双向数据绑定。


该语句实际上隐藏了其实现细节:


如果需要做一些不同的处理,就不能使用 [(ngModel)] 语法,而应写成扩展的方式:


ngModel 指令只能用在支持 ControlValueAccessor 的元素上。

内置结构型指令(Built-in structural directives)

结构型指令负责 HTML 布局。

常见的内置结构型指令:

  • NgIf conditionally add or remove an element from the DOM
  • NgFor repeat a template for each item in a list
  • NgSwitch a set of directives that switch among alternative views

NgIf

You can add or remove an element from the DOM by applying an NgIf directive to that element (called the host elment).

示例:


When the isActive expression returns a truthy value, NgIf adds the HeroDetailComponent to the DOM. When the expression is falsy, NgIf removes the HeroDetailComponent from the DOM, destroying that component and all of its sub-components.

别忘了 ngIf 前面的星号( * )。

这和显示/隐藏不是一回事。

我们也可以通过类绑定或样式绑定来显示或隐藏一个元素。但隐藏子树和用 NgIf 排除子树是截然不同的。

当隐藏子树时,它仍然留在 DOM 中。当 NgIffalse 时 Angular 从 DOM 中物理地移除了子树,它销毁了子树中的组件及其状态,也潜在释放了可观的资源。

防范空指针错误

NgIf 指令通常会用来防范空指针错误。 而显示/隐藏的方式是无法防范的。

Hello, {{currentHero.name}}
Hello, {{nullHero.name}}

currentHero 的名字只有当存在 currentHero 时才会显示出来。 而 nullHero 永远不会显示。

NgFor

NgFor 是一个重复器指令——显示列表项的一种方式。你先定义了一个 HTML 块,它规定了单个条目应该如何显示。然后你告诉 Angular 把这个块当做模板,渲染列表中的每个条目。
NgFor is a repeater directive — a way to present a list of items. You define a block of HTML that defines how a single item should be displayed. You tell Angular to use that block as a template for rendering each item in the list.

例子1:

{{hero.name}}

例子2:


别忘了 ngFor 前面的星号( * )。

赋值给 *ngFor 的字符串不是模板表达式,它是一个微语法 —— 由 Angular 自己解释的小型语言。

字符串 "let hero of heroes" 的含义:

取出 heroes 数组中的每个英雄,把它存入局部变量 hero 中,并在每次迭代时对模板 HTML 可用。
Take each hero in the heroes array, store it in the local hero looping variable, and make it available to the templated HTML for each iteration.

模板输入变量 (Template input variables)

The let keyword before hero creates a template input variable called hero.

*ngFor 的索引 (index)

The index property of the NgFor directive context returns the zero-based index of the item in each iteration.

{{i + 1}} - {{hero.name}}

更多内容参见 NgFor API

*ngFor with trackBy

ngFor 指令有时候会性能较差,特别是在大型列表中。对一个条目的一丁点改动、移除或添加,都会导致级联的 DOM 操作。

有了 trackBy,则只有 id 发生改变才会触发元素替换。

在组件中添加方法:

trackByHeroes(index: number, hero: Hero): number { return hero.id; }

使用 trackBy

({{hero.id}}) {{hero.name}}

NgSwitch

NgSwitch can display one element from among several possible elements, based on a switch condition.

NgSwitch 由三个指令组成:

  • 属性型指令 NgSwitch
  • 结构型指令 NgSwitchCase
  • 结构型指令 NgSwitchDefault

示例:

NgSwitch 指令在增加或移除组件元素 (component elements) 时尤其有用。

模板引用变量 (Template reference variables) #var

模板引用变量通常是一个模版中的对 DOM 元素的一个引用。
A template reference variable is often a reference to a DOM element within a template.

使用井号 # (或 ref-)来声明一个模板引用变量。The#phone declares a phone variable on an element.


或者写成

你可以在模板中的任意位置引用该模板引用变量。







说明:phone refers to the phone number box. The phone button click handler passes the *input *value to the component's callPhone method.

模板引用变量如何获取自身的值?

通常,如果一个元素声明了一个模板引用变量,那么 Angular 会将模板引用变量的值设置为该元素的值。
In most cases, Angular sets the reference variable's value to the element on which it was declared.

示例:

{{submitMessage}}

If Angular hadn't taken it over when you imported the FormsModule, it would be the HTMLFormElement. The heroForm
is actually a reference to an Angular NgForm directive with the ability to track the value and validity of every control in the form.

The native

element doesn't have a form property. But the NgForm directive does, which explains how you can disable the submit button if the heroForm.form.valid is invalid and pass the entire form control tree to the parent component's onSubmit method.

注意

模板引用变量 (template reference variable) (#phone) 与模板输入变量 (template input variable) (*ngFor 中的 let phone) 并不相同。详见结构型指令。

输入输出属性 @Input@Output

绑定目标与绑定源的区别:

  • 绑定的目标是在 = 左侧的部分, 则是在 = 右侧的部分。
  • 绑定的目标是绑定符:[]()[()] 中的属性或事件名, 源则是引号 " " 中的部分或插值符号 {{}} 中的部分。
  • 指令中的每个成员都会自动在绑定中可用。 不需要特别做什么,就能在模板表达式或语句中访问指令的成员。
  • 访问目标指令中的成员则受到限制。 只能绑定到那些显式标记为输入输出的属性。

iconUrlonSave 是绑定源



HeroDetailComponent.hero 是属性绑定的目标。 HeroDetailComponent.deleteRequest 是事件绑定的目标。



声明输入和输出属性

目标属性必须被显式的标记为输入或输出。

方法1:使用装饰器 @Input()@Output()

@Input()  hero: Hero;
@Output() deleteRequest = new EventEmitter();

方法2:通过元数据数组。

@Component({
  inputs: ['hero'],
  outputs: ['deleteRequest'],
})

两种方法不可同时使用。

输入还是输出?

输入属性通常接收数据值。 输出属性暴露事件生产者。
Input properties usually receive data values. Output properties expose event producers.

输入和输出这两个词是从目标指令的角度来说的。

  • HeroDetailComponent 角度来看,HeroDetailComponent.hero 是个输入属性, 因为数据流从模板绑定表达式流入那个属性。
  • HeroDetailComponent 角度来看,HeroDetailComponent.deleteRequest 是个输出属性, 因为事件从那个属性流出,流向模板绑定语句中的处理器。

给输入输出属性起别名

方法1:把别名传进 @Input / @Output 装饰器,就可以为属性指定别名:

@Output('myClick') clicks = new EventEmitter(); //  @Output(alias) propertyName = ...

方法2:在 inputsoutputs 数组中为属性指定别名。 语法(属性名:别名)。

@Directive({
  outputs: ['clicks:myClick']  // propertyName:alias
})

模板表达式操作符

管道操作符 |

管道是一个简单的函数,它接受一个输入值,并返回转换结果。

Title through uppercase pipe: {{title | uppercase}}

管道操作符会把它左侧的表达式结果传给它右侧的管道函数。

更多内容见管道。

还可以通过多个管道串联表达式:


Title through a pipe chain: {{title | uppercase | lowercase}}

还能对管道使用参数:


Birthdate: {{currentHero?.birthdate | date:'longDate'}}

json 管道对调试绑定特别有用:

{{currentHero | json}}

输出结果:

{ "id": 0, "name": "Hercules", "emotion": "happy",
  "birthdate": "1970-02-25T08:00:00.000Z",
  "url": "http://www.imdb.com/title/tt0065832/",
  "rate": 325 }

安全导航操作符 ( ?. ) 和空属性路径

Angular 的安全导航操作符 (?.) 用来保护出现在属性路径中 null 和 undefined 值。示例:

The current hero's name is {{currentHero?.name}}

说明:当 currentHero 为空时,保护视图渲染器,让它免于失败。

显示一个空 (null) 英雄的 name 示例:

The null hero's name is {{nullHero.name}}

:marked
  JavaScript throws a null reference error, and so does Angular:
JavaScript 抛出了空引用错误,Angular 也是如此:code-example(format="nocode").
  TypeError: Cannot read property 'name' of null in [null].

currentHero 为空的时候,应用崩溃了,整个视图都不见了。

笨重的解决办法1:


The null hero's name is {{nullHero.name}}

笨重的解决办法2:

The null hero's name is {{nullHero && nullHero.name}}

正确的解决办法:


The null hero's name is {{nullHero?.name}}

总结

透彻理解模板语法对开发至关重要。

你可能感兴趣的:(Angular4-学习笔记-4-模板语法)