Skip to main content

相关知识

管道

  • 每个绑定都有自己的管道实例。

  • 每个管道实例都有自己数据。

内置管道

  • date

  • json

  • async

  • currency

  • persent

  • decimal

  • lowerCase

  • upperCase

....

使用

{{ 组件成员 | 管道名称 }}

传参

管道名称 : 参数值,多个参数使用多个冒号分隔,参数可以是任何合法的 JS 表达式/组件成员

可以使用管道运算符|来串联多个管道

自定义管道

import { Pipe, PipeTransform } from "@angular/core";

@Pipe({
name: "pow"
})
export class PowPipe implements PipeTransform {
transform(value: number, pow = 1): number {
return value ** pow;
}
}

shared.module.ts 导入

在模版中使用

<!-- 16 -->
{{ 2 | pow: 4 }}

管道的执行时机

angular 会在每次变更检测的时候都去执行管道(事实是,只有非纯管道才会在 angular 每次的变更检测时都执行,纯管道只会在绑定的值发生变化或者引用发生变化的时候执行)

变更检测的执行时机

在每次 DOM 事件(事件、定时器、网络请求)之后运行

管道的变更检测

纯管道:检测原始类型和对象引用的纯变更

默认情况下,管道会被定义成纯的(pure)。这样 Angular 只有在检测到输入值发生了纯变更时才会执行该管道。纯变更是对原始输入值(比如 String、Number、Boolean 或 Symbol )的变更,或是对对象引用的变更(比如 Date、Array、Function、Object)。

非纯管道:检测复合对象中的非纯变更

要在复合对象内部进行更改后执行自定义管道(例如更改数组元素),就需要把管道定义为 impure 以检测非纯的变更。每当按键或鼠标移动时,Angular 都会检测到一次变更,从而执行一个非纯管道。(效率很低)

@Pipe({
name: 'pipeName',
pure: false
})

AsyncPipe

AsyncPipe 可以将一个 Observable 对象作为输入,自动订阅这个 Observable,并且在组件销毁的时候自动取消订阅。不需要我们在组件的 class 文件中进行手动操作。AsyncPipe 是一个非纯管道

管道操作符的优先级

管道操作符要比三目运算符(?:)的优先级高,这意味着 a ? b : c | x 会被解析成 a ? b : (c | x)

属性、类、style 绑定

属性绑定

[属性名]='成员/表达式' / 属性名='{{ 成员/表达式 }}' / 属性名='值'(此时右侧会被当作为字符串)

无论是哪种形式,都需要在模版语句上加引号

所以我能不能说模版语句需要使用引号包裹

clospan 和 colSpan

最容易混淆的地方是 colspan 这个 Attribute 和 colSpan 这个 Property ????。请注意,这两个名称只有一个字母的大小写不同。

<tr>
<td colspan="{{1 + 1}}">Three-Four</td>
</tr>

报错:

Template parse errors:
Can't bind to 'colspan' because it isn't a known built-in property

如消息中所示,<td> 元素没有 colspan Property。这是正确的,因为 colspan 是一个 Attribute — colSpan(带大写 S)才是相应的 Property。插值和 Property 绑定只能设置 Property,不能设置 Attribute。

而原生 html 上的属性是 attribute,其对应的 angular 的 property 就是其小驼峰的形式

设置指令的属性

为指令绑定属性的时候需要将指令放在[]中

<p [ngClass]="classes">
[ngClass] binding to the classes property making this blue
</p>

属性绑定与安全性

属性绑定可以帮助确保内容的安全。

evilTitle = 'Template <script>alert("evil never sleeps")</script> Syntax';

组件模板对内容进行插值

<p>
<span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span>
</p>

浏览器不会处理 HTML,而是原样显示它,如下所示

"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.

Angular 不允许带有 <script> 标记的 HTML,既不能用于插值也不能用于属性绑定,这样就会阻止运行 JavaScript。

但是,在以下示例中,Angular 在显示值之前会先对它们进行无害化处理

<!--
Angular generates a warning for the following line as it sanitizes them
WARNING: sanitizing HTML stripped some content (see https://g.co/ng/security#xss).
-->
<p>
"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil
title.
</p>

插值处理 <script> 标记的方式与属性绑定的方式不同,但这两种方法都可以使内容无害。以下是经过无害化处理的 evilTitle 示例的浏览器输出。

"Template Syntax" is the property bound evil title.

也就是说插值和属性绑定这两种方式都可以对内容进行安全处理,只不过方式不同,前者是对标签进行转译,后者是标签中的内容不会被展示

属性绑定和插值

通常,插值和属性绑定可以达到相同的结果。

<p><img src="{{itemImageUrl}}" /> is the <i>interpolated</i> image.</p>
<p><img [src]="itemImageUrl" /> is the <i>property bound</i> image.</p>

<p>
<span>"{{interpolationTitle}}" is the <i>interpolated</i> title.</span>
</p>
<p>
"<span [innerHTML]="propertyTitle"></span>" is the
<i>property bound</i> title.
</p>

将数据值渲染为字符串时,可以使用任一种形式,只是插值形式更易读。但是,要将元素属性设置为非字符串数据值时,必须使用属性绑定

Attribute 绑定、类绑定和样式绑定

attribute 绑定

尽可能使用 property 绑定,如果你没法使用 property 绑定的时候在使用 attribute 绑定(绑定原生 HTML 元素的属性,或者 data 自定义属性)

在 Attribute 名称前面加上前缀 attr,后跟一个点 .原生html属性名。然后,使用解析为字符串的表达式设置 Attribute 值

<p [attr.attribute-you-are-targeting]="expression"></p>
<!--  expression calculates colspan=2 -->
<tr>
<td [attr.colspan]="1 + 1">One-Two</td>
</tr>

当表达式解析为 null 或 undefined 时,Angular 会完全删除该 Attribute。

绑定到 class attribute

单个 class 类绑定

前缀 class 后跟一个点和 CSS 类的名称,例如 [class.sale]="onSale"。onSale 为真值时添加类,在表达式为假值时(undefined 除外)删除类

多个 class 类绑定

[class]="classExpression" classExpression的取值;ngClass也可以做同样的事情。ngClass 只支持表达式绑定

  • 用空格分隔的类名字符串

  • 以类名作为键名并将真或假表达式作为值的对象

classExpression = { foo: true, bar: false };
  • 类名的数组

对于引用类型,则需要引用发生改变,才可以更新 class

绑定属性到 style attribute

绑定单一样式

前缀 style 后跟一个点和 CSS style Attribute 的名称,例如 [style.width]="width"。 Angular 会将该 Attribute 设置为绑定表达式的值,这个值通常是一个字符串。

可以使用中划线或者是小驼峰

<nav [style.background-color]="expression"></nav>

<nav [style.backgroundColor]="expression"></nav>
绑定多个样式

[style]="styleExpression" 。styleExpression 可以是如下格式之一:ngStyle也可以做同样的事情。ngStyle 只支持对象绑定,不支持字符串

  • 样式的字符串列表,例如 "width: 100px; height: 100px; background-color: cornflowerblue;"

  • 一个对象,其键名是样式名,其值是样式值,比如 {width: '100px', height: '100px', backgroundColor: 'cornflowerblue'}

当把 [style] 绑定到对象表达式时,该对象的引用必须改变,这样 Angular 才能更新这个类列表

使用[style]来代替[ngStyle]的指令绑定,因为未来 angular 可能要移除[ngStyle]

样式绑定的优先级

  • 类或样式绑定越具体,其优先级就越高。 以下优先级由高到低
  1. 属性绑定(例如,<div [class.foo]="hasFoo"><div [style.color]="color">

  2. 映射表绑定(例如,<div [class]="classExpr"><div [style]="styleExpr">

  3. 静态值(例如 <div class="foo"><div style="color: blue">

  • 绑定总是优先于静态属性,及直接使用 html 元素的 class 属性/style 属性添加的样式

事件绑定

(js事件名 - on) = '事件处理函数'

eg: (click)='onClick()'

等号左侧括号内的目标事件名和右侧引号内的模板语句组成

EventEmitter自定义事件

  1. 组件创建一个 EventEmitter 并将其对外暴露为属性。

  2. 然后,该组件调用 EventEmitter.emit(data) 发出事件,传入消息数据,该消息数据可以是任何东西。

  3. 父组件通过绑定到该属性来监听事件,并通过传入的 $event 对象接收数据。

// This component makes a request but it can't actually delete a hero.
@Output() deleteRequest = new EventEmitter<Item>();

delete() {
this.deleteRequest.emit(this.item);
this.displayNone = this.displayNone ? '' : 'none';
this.lineThrough = this.lineThrough ? '' : 'line-through';
}

父组件

<app-item-detail (deleteRequest)="deleteItem($event)" [item]="currentItem"></app-item-detail>

$event在原生事件中代码Event对象;在自定义事件中就是我们emit的数据

双向绑定

[(ngModel)]

Angular 的双向绑定语法是方括号和圆括号的组合 [()]。[] 进行属性绑定,() 进行事件绑定

双向绑定工作原理

@Output() 属性的名字必须遵循 inputChange 模式,其中 input 是相应 @Input() 属性的名字。例如,如果 @Input() 属性为 size ,则 @Output() 属性必须为 sizeChange 。

export class SizerComponent {

@Input() size!: number | string;
@Output() sizeChange = new EventEmitter<number>();

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);
}
}
<app-sizer [(size)]="fontSizePx"></app-sizer>
fontSizePx = 16; // 初始化app-sizer的size属性

sizeChange事件将修改副组件的fontSizePx属性,同理父组件的fontSizePx属性也会更新子组件的size属性。

双向绑定语法是属性绑定和事件绑定的组合的简写形式。拆成单独的属性绑定和事件绑定形式的 SizerComponent 代码如下:

<app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer>

表单的双向数据绑定使用 ngModel

模版变量

使用 #变量名 在模版文件中获取组件实例或者DOM元素

语法

在模板中,要使用井号 # 来声明一个模板变量。

<input #phone placeholder="phone number" />

可以在该组件模版的任意地方使用该变量

<input #phone placeholder="phone number" />

<!-- lots of other elements -->

<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>

Angular 是如何为模板变量赋值的

  • 如果在组件上声明变量,该变量就会引用该组件实例。
  • 如果在标准的 HTML 标记上声明变量,该变量就会引用该元素。
  • 如果你在 <ng-template> 元素上声明变量,该变量就会引用一个 TemplateRef 实例来代表此模板。
  • 如果该变量在右侧指定了一个名字,比如 #var="ngModel" ,那么该变量就会引用所在元素上具有这个 exportAs 名字的指令或组件。

ngForm指令与模版引用变量

<form #itemForm="ngForm" (ngSubmit)="onSubmit(itemForm)">
<label for="name">Name</label>
<input type="text" id="name" class="form-control" name="name" ngModel required />
<button type="submit">Submit</button>
</form>

<div [hidden]="!itemForm.form.valid">
<p>{{ submitMessage }}</p>
</div>

使用了 NgForm 之后,itemForm 就是对 NgForm 指令的引用

模版引用变量的作用域

可以在包含此模板变量的模板中的任何地方引用它。而 结构型指令(如 ngIf 和 ngFor 或 <ng-template> 同样充当了模板的边界。你不能在这些边界之外访问其中的模板变量。

同名变量在模板中只能定义一次

模版输入变量

<ng-template #hero let-hero let-i="index" let-odd="isOdd">
<div [class]="{'odd-row': odd}">{{i}}:{{hero.name}}</div>
</ng-template>

let-hero中的hero以及i以及isOdd都是模版输入变量,仅仅能在ng-template内部访问,hero则是模版引用变量,在整个模版中都是可以访问的