第3章指令 3.1指令概述 3.1.1指令含义 Angular的模板是动态的,当Angular渲染它们时,会根据模板中的指令给出的指示对DOM进行转换。指令是为Angular应用中的元素添加额外行为的类,是一个带有@Directive()装饰器的类。从技术角度上说,组件也是指令。由于组件对Angular应用来说非常重要和独特,因此Angular专门定义了@Component()装饰器,它使用一些面向模板的特性扩展了@Directive()装饰器,即组件是带有模板特性的指令。 Angular本身定义了一系列结构型指令、属性型指令等内置指令,可以管理表单、列表、样式以及用户界面上要让用户看到的其他内容; 也可以使用@Directive()装饰器定义自定义指令。 3.1.2指令类型 指令可以分为组件、属性型指令、结构型指令等不同类型。为了区分,本书将组件和指令分开介绍。属性型指令可以更改元素、组件或其他指令的外观或行为; 结构型指令可以通过添加和删除或替换DOM元素来更改DOM布局。 3.1.3指令和模板的关系 指令的元数据把它所装饰的指令类和一个选择器(selector)关联起来,selector用来把关联的指令插入HTML模板中。在模板中,指令通常作为特性出现在模板元素(HTML标签)上,可能仅仅作为名字出现,也可能作为赋值目标或绑定目标出现。在模板中,它们看起来就像普通的HTML特性一样。 3.2内置属性型指令 3.2.1内置属性型指令说明 属性型指令会监听并修改HTML元素和组件的行为、attribute和property。许多NgModule(如RouterModule和FormsModule)都定义了自己的属性型指令。最常见的属性型指令包括添加和删除一组CSS类的指令NgClass、添加和删除一组HTML样式的指令NgStyle、将数据双向绑定添加到HTML表单元素的指令NgModel等指令。内置指令只会使用公开API。它们不会访问任何不能被其他指令访问的私有API。 3.2.2NgClass说明 要添加或删除单个类,优先使用类绑定而不是NgClass。用NgClass可以同时添加或删除多个CSS类。可以将NgClass与表达式一起使用,即在要设置样式的元素上添加[ngClass]并将其设置为等于某个表达式。如代码
This div is special
中将isSpecial设置为布尔值true后,NgClass就会把special类应用于
上。 要想将NgClass与方法一起使用,需要将方法添加到组件中。如例31中的setCurrentClasses()方法使用一个对象来设置属性currentClasses,该对象根据canSave、isUnchanged、isSpecial等组件属性为true或false来添加或删除三个类。该对象的每个键(key)(如saveable)都是一个类名。如果键(类名)的取值为true,则NgClass添加该类。如果键(类名)的取值为false,则NgClass删除该类。于是,在模板中把NgClass属性绑定到currentClasses,根据它来设置此元素的CSS类。在例31中,Angular会在初始化以及发生更改的情况下应用这些类,即在ngOnInit()方法中进行初始化以及通过单击按钮更改相关属性时调用setCurrentClasses()方法。 【例31】创建文件insidedirective.component.ts的代码 ,演示本章的基础知识点。 import {Component} from '@angular/core'; import {Item} from "./item"; @Component({ selector: 'root',//'app-inside-directive', template: `
NgClass绑定

currentClasses is {{currentClasses | json}}

This div is initially saveable, unchanged, and special.
This div should be {{ canSave ? "": "not"}} saveable, {{isUnchanged ? "unchanged" : "modified"}} and {{isSpecial ? "": "not"}} special after clicking "Refresh".


This div is special
Helpful study course
Study course

NgStyle绑定
This div is x-large or smaller.

[ngStyle] binding to currentStyles - CSS property names

currentStyles is {{currentStyles | json}}

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

| |

This div should be {{ canSave ? "italic": "plain"}}, {{isUnchanged ? "normal weight" : "bold"}} and, {{isSpecial ? "extra large": "normal size"}} after clicking "Refresh".

NgModel双向绑定

NgModel examples

Current item name: {{currentItem.name}}


Hello, {{nullCustomer.id}}
Hello, {{nullCustomer2}}
{{i + 1}} - {{item.name}}
({{item.id}}) {{item.name}}

I turned the corner and saw {{item.name}}. I waved and continued on my way.


NgSwitch Binding

Pick your favorite item

Are you as bright as {{currentItem.name}}?

Highlight me!

Pick a highlight color

Green Yellow Cyan

Highlight me!

不会显示2: {{ 1 + 1 }}

This should not evaluate: {{ 1 +1 }}, but will highlight yellow.

(A) This paragraph is displayed because the condition is false.

(B) Although the condition is true, this paragraph is displayed because appUnless is set to false.

The condition is currently {{condition}}.

`, }) export class InsideDirectiveComponent { currentClasses: Record = {}; currentStyles: Record = {}; canSave: boolean=true; isUnchanged: boolean=true; isSpecial: boolean=true; item!: Item; items: Item[] = []; currentItem!: Item; nullCustomer: any = { id:21, name:'zsf' }; nullCustomer2: any; color: any='red'; condition: any =false; resetItems() { this.items = Item.items.map(item => item.clone()); this.currentItem = this.items[0]; this.item = this.currentItem; } ngOnInit() { this.resetItems(); this.setCurrentClasses(); this.setCurrentStyles(); } setCurrentClasses() { this.currentClasses = { saveable: this.canSave, modified: !this.isUnchanged, special: this.isSpecial }; } setCurrentStyles() { this.currentStyles = { 'font-style':this.canSave? 'italic' : 'normal', 'font-weight': !this.isUnchanged ? 'bold' : 'normal', 'font-size': this.isSpecial? '24px' : '12px' }; } getValue(event: Event): string { return (event.target as HTMLInputElement).value; } setUppercaseName(name: string) { this.currentItem.name = name.toUpperCase(); } trackByItems(index: number, item: Item): number { return item.id; } } 3.2.3NgStyle说明 可以用NgStyle根据组件的状态同时设置多个内部样式。要使用NgStyle,就要向组件添加一个方法。如例31中,setCurrentStyles()方法基于该组件canSave、isUnchanged、isSpecial等属性的状态,用一个定义了fontstyle、fontweight、fontsize三个样式的对象设置了currentStyles属性。设置元素的样式,需要将ngStyle属性绑定到currentStyles。在例31中,Angular会在初始化以及发生更改的情况下应用这些类。完整的示例会在ngOnInit()方法中进行初始化以及通过单击按钮更改相关属性时调用setCurrentClasses()方法。 3.2.4NgModel说明 可以用NgModel指令显示数据属性,并在用户进行更改时更新该属性。导入FormsModule,并将其添加到NgModule的imports列表中,如例32所示。在HTML的
标签上添加[(ngModel)]绑定并将其设置为等于属性,如代码中设置数据绑定属性。要自定义配置,可以编写可展开的表单,该表单将属性绑定和事件绑定分开。使用属性绑定来设置属性,并使用事件绑定来响应更改。NgModel指令适用于值访问器接口ControlValueAccessor支持的元素。Angular为HTML表单所有基本元素提供了值访问器接口。要将[(ngModel)]应用于非表单型内置元素或第三方自定义组件,必须编写一个值访问器接口。编写Angular组件时,如果根据Angular的双向绑定语法命名value和event属性,则不需要用值访问器接口或NgModel。 【例32】创建文件appdirectiveexample.module.ts的代码, 定义路由并声明组件和指令。 import {NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {RouterModule} from "@angular/router"; import {DirectiveexamplesComponent} from "./directiveexamples.component"; import {FormsModule} from "@angular/forms"; import {InsideDirectiveComponent} from "./inside-directive.component"; import { BestItemComponent, DeviceItemComponent, LostItemComponent, StoutItemComponent, UnknownItemComponent } from "./item-switch.component"; import {HighlightDirective} from "./highlight.directive"; import {UnlessDirective} from "./unless.directive"; @NgModule({ imports: [ BrowserModule, RouterModule.forRoot([ {path: 'insidedirective', component: InsideDirectiveComponent}, {path: 'directiveexample', component: DirectiveexamplesComponent}, ]), FormsModule, ], declarations: [ InsideDirectiveComponent, StoutItemComponent, BestItemComponent, DeviceItemComponent, LostItemComponent, UnknownItemComponent, HighlightDirective, UnlessDirective, DirectiveexampleComponent ], }) export class AppDirectiveexampleModule { } 3.3内置结构型指令 3.3.1内置结构型指令说明 结构型指令的职责是进行HTML布局,并通过添加、移除和操纵它们所附加的宿主元素(DOM节点)来塑造或重塑DOM结构。由于结构型指令会在DOM中添加和删除节点,因此每个元素只能应用一个结构型指令。常见的内置结构型指令包括从模板中创建或销毁子视图的NgIf指令、为列表中的每个条目重复渲染一个节点的NgFor指令和一组在备用视图之间切换的NgSwitch指令。 3.3.2NgIf说明 在宿主元素上用 NgIf指令可以通过条件决定是否 添加或删除宿主元素。如果NgIf指令为false,则Angular将从DOM中移除对应的元素及其后代。然后,Angular会销毁其组件,从而释放内存和资源。要添加或删除元素,需要将*ngIf绑定到条件表达式。如代码中将*ngIf绑定到isActive,当isActive表达式返回true值时,NgIf指令会把appitemdetail所在组件添加到DOM中。在默认情况下,NgIf指令会阻止显示已绑定到空值的元素。如要使用NgIf指令保护
,就需要将代码“*ngIf="yourProperty"”添加到
,修改之后的结果可能为
Hello, {{currentCustomer.name}}
。如果属性currentCustomer为 null,则Angular不会显示
(注意,是整个
都不会显示)。 3.3.3NgFor说明 NgFor指令用于显示条目列表。定义一个HTML块,该块用于决定Angular如何渲染单个条目,如代码
{{item.name}}
中要列出的条目item,把字符串"let item of items"赋给*ngFor。字符串"let item of items"用于指示Angular执行将items中的每个条目存储在局部循环变量item中、让每个条目在每次迭代时的模板HTML中都可用、将"let item of items"转换为环绕宿主元素的、对列表中的每个item重复等操作。Angular会将指令转换为,然后反复使用此模板为列表中的每个item创建一组新的元素和绑定。 要复写某个组件元素,可以将*ngFor应用于其选择器,如代码中的选择器为。在宿主元素的后代中,该选择器用以访问条目的属性,将item通过绑定传递给组件的item属性。 可以在模板输入变量中 获取*ngFor的index索引并在模板中使用它。在*ngFor中,添加一个分号(;)和let i=index的简写形式,如代码
{{i + 1}}  {{item.name}}
即把index赋予一个名为i的变量中,并将其与条目名称一起显示。NgFor指令上下文的index属性在每次迭代中都会返回该条目的从零开始的索引号。 3.3.4NgIf、NgFor和容器 若要在特定条件为true时重复某个HTML块,则可以将*ngIf放在*ngFor元素的容器元素上。它们之一或两者都可以是,这样就不必引入额外的HTML层次了。 通过跟踪对条目列表的更改,可以减少应用对服务器的调用次数。使用*ngFor的trackBy属性,Angular只能更改和重新渲染已更改的条目,而不必重新加载整个条目列表。如向某组件添加一个trackByItems()方法,该方法返回NgFor指令应该跟踪的值(item.id); 如果浏览器已经渲染过某个id,Angular就会跟踪它而不会重新向服务器查询相同的id。如果没有trackBy属性,就会由触发完全的DOM元素替换。有了trackBy属性,只有修改了id的按钮才会触发元素替换。 Angular的是一个分组元素,它不会干扰样式或布局,因为Angular不会将其放置在DOM中。当没有单个元素承载指令时,可以使用。同时,要有条件地排除