Angular官网学习笔记
一、Angular概述
**什么是Angular:**一个基于TypeScript构建的开发平台包括:
- 一个基于组件的框架,用于构建可伸缩的Web应用
- 一组完美集成的库,涵盖各种功能,包括路由、表单管理、客户端-服务器通信等
- 一套开发工具,帮助开发、构建、测试、更新代码
**优势:**让更新更容易,可以用最小的成本升级到最新的Angular版本。
1.1知识要点:
1.1.1 组件
组件是构成应用的砖块。 由三个部分组成:带有@Component()装饰器的TypeScript类、Html模板和样式文件。 @Component()装饰器的TypeScript类 @Component()装饰器会指定一下信息
- 一个CSS选择器,用于定义如何在模板中使用组件。
- 一个HTML模板,用于指示Angular如何渲染此组件
- 一组可选的CSS样式,用于定义模板中HTML的外观
1.1.2 模板
每个组件都有一个HTML模板,用于声明该组件的渲染方式。使用内联或文件路径定义 Angular使用额外的语法扩展了HTML,文本插值(向组件中动态的插入动态值) ,支持属性绑定 、事件监听 、指令为模板添加额外功能 。
1.1.3 依赖注入
满足声明TypeScript类的依赖项,无需操心如何实例化。 指某个类执行其功能所需的服务或对象。依赖项注入(DI)是一种设计模式,此设计模式中类会从外部源请求依赖项而不是创建。
1.1.4 Angular CLI
常用CLI命令: ng build :把Angular应用编译到一个输出目录中。 ng serve :构建你的应用并启动开发服务器,当有文件变化就重新构建 ng generate :基于原理图生成或修改某些文件 ng test :在指定的项目上运行单元测试。 ng e2e :构建一个Angular应用并启动开发服务器,然后运行端到端测试。
1.1.5 自带库
常用库: Angular 路由器 :高级的客户侧导航功能与基于Angular组件的路由机制。支持惰性加载、嵌套路由、自定义路径匹配规则等。 Angular 表单 :同意的表单填报与验证体系。 Angular HttpClient :支持高级的客户端-服务器通讯。 Angular 动画 :用于驱动基于应用状态的动画。 Angular PWA :用于构建渐进式Web应用(PWA)的工具,包括Service Worker和Web应用清单。 Angular 原理图 :一些搭建脚手架、重构、升级的自动化工具。用于简化大规模应用的开发。
二、详解Angular功能
2.1 组件
创建组件
ng g component componentName
2.1.1 组件生命周期
生命周期伴随着变更检测,Angular会检查数据绑定属性何时发生变化,并按需更新视图和组件实例。直至销毁组件实例并移除它渲染的模板 组件与指令的生命周期唯一的区别就是指令没有AfterContentInit和AfterContentChecked,因为指令没有投影,组件或指令发生变更检测时都会调用三个变更检测方法:ngDoCheck、ngAfterContentChecked、ngAfterViewChecked 生命周期顺序: constructor() :初始化类,主要用于注入依赖,在所有生命周期钩子之前执行,用于依赖注入或执行简单的数据初始化操作。 ngOnChanges() :触发顺序在ngOnInit 之前,当绑定有输入属性或输入属性有发生改变时触发,接收当前和上一个属性值的SimpleChanges对象,。 ngOnInit() :顺序在ngOnChages() 之后。第一次显示数据绑定和设置指令/组件的输入属性后触发,即使没触发ngOnChages() 也会触发。只触发一次。 ngDoCheck() :自定义的方法,用于检测和处理值的改变。 ngAfterContentInit() :在组件内容初始化之后调用。 ngAfterContentChecked() :组件每次检查内容时调用。 ngAfterViewInit() :组件相应的视图初始化之后调用。 ngAfterViewCeched() :组件每次检查视图时调用 ngOnDestory() :指令销毁时调用,此处可以反订阅观察对象和分离事件处理器,防止内存泄漏。
1
响应视图的变更 当Angular再变更检测期间遍历视图树时,需要确保子组件中的某个变更不会尝试更改父组件中的属性,因为单向数据流 的工作原理如此,这样的更改无法正常渲染。 如果需要做一个上述操作,必须触发一个新的变更检测周期。 举个栗子:
@Component({
selector: 'app-child-view',
template: `
<label for="hero-name">Hero name: </label>
<input type="text" id="hero-name" [(ngModel)]="hero">
`
})
export class ChildViewComponent {
hero = 'Magneta';
}
template: `
<div>child view begins</div>
<app-child-view></app-child-view>
<div>child view ends</div>
`
export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
private prevHero = '';
@ViewChild(ChildViewComponent) viewChild!: ChildViewComponent;
ngAfterViewInit() {
this.logIt('AfterViewInit');
this.doSomething();
}
ngAfterViewChecked() {
if (this.prevHero === this.viewChild.hero) {
this.logIt('AfterViewChecked (no change)');
} else {
this.prevHero = this.viewChild.hero;
this.logIt('AfterViewChecked');
this.doSomething();
}
}
private doSomething() {
const c = this.viewChild.hero.length > 10 ? "That's a long name" : '';
if (c !== this.comment) {
this.logger.tick_then(() => this.comment = c);
}
}
}
在视图合成完之后,就会触发AfterViewInit() 和AfterViewCheched() 钩子,如果修改了这段代码,这个钩子就会立即修改该组件的数据绑定属性comment,测试会报错。 this.logger.tick_then(() => this.comment = c); 会把日志的的更新工作推迟一个浏览器JS周期,也就会触发一个新的变更检测周期。
响应被投影内容的变更 内容投影时从组件外部导入HTML内容,并插入在组件模板中指定位置 投影内容会触发AfterContentInit() 和AfterContentChecked() 需要在父组件中导入
template: `
<div>projected content begins</div>
<ng-content></ng-content>
<div>projected content ends</div>
`
`<after-content>
<app-child></app-child>
</after-content>`
AfterContent和AfterView类似:不同在于子组件类型不同:
- AfterView钩子关心的是
ViewChildren ,这些子组件的元素标签会出现在该组件的模板里面 - AfterContent:钩子关心的是
ContentChildren ,这些子组件被Angular投影进该组件中。 该组件的doSomething() 会立即更新该组件的数据绑定属性comment ,无需延迟更新以确保正确渲染。
2.2 视图封装
将组件的样式封装在组建的宿主元素中,避免影响其他应用程序。 分三种模式: ViewEncapsulation.ShadowDom :组件的样式被包含在这个ShadowDom中,外部样式进不来,内部样式出不去 ViewEncapsulation.Emulated :样式局限在组件视图,全局样式可以进来,内部样式出不去 ViewEncapsulation.None :不使用视图封装,Angular会把CSS添加到全局样式中,全局样式可以进来,内部样式可以出去。 使用:
@Component({
selector: 'app-no-encapsulation',
template: `
<h2>None</h2>
<div class="none-message">No encapsulation</div>
`,
styles: ['h2, .none-message { color: red; }'],
encapsulation: ViewEncapsulation.None,
})
export class NoEncapsulationComponent { }
2.3 组件交互
2.3.1 通过输入型绑定 父传子
在子组件中添加@Input() 装饰器
export class HeroChildComponent {
@Input() hero!: Hero;
@Input('master') masterName = '';
}
<app-hero-child
*ngFor="let hero of heroes"
[hero]="hero"
[master]="master">
</app-hero-child>
export class HeroParentComponent {
heroes = HEROES;
master = 'Master';
}
2.3.2 通过setter截听输入输入属性值变化
使用一个输入属性的setter,拦截父组件中值的变化
@Input()
get name(): string { return this._name; }
set name(name: string) {
this._name = (name && name.trim()) || '<no name set>';
}
private _name = '';
父组件与上述一致
2.3.3 通过ngOnChanges()来截听输入属性值变化
当需要监听多个、交互式输入属性时可以使用该方法
@Input() major = 0;
@Input() minor = 0;
changeLog: string[] = [];
ngOnChanges(changes: SimpleChanges) {
const log: string[] = [];
for (const propName in changes) {
const changedProp = changes[propName];
const to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) {
log.push(`Initial value of ${propName} set to ${to}`);
} else {
const from = JSON.stringify(changedProp.previousValue);
log.push(`${propName} changed from ${from} to ${to}`);
}
}
this.changeLog.push(log.join(', '));
}
父组件不变
2.3.4 父组件监听子组件事件
子组件暴露一个EventEmitter属性,当事件发生,子组件利用该属性emits(向上弹射)事件,父组件绑定到这个事件属性,并在事件发生时做出相应。 使用@Output() 装饰器
@Input() name = '';
@Output() voted = new EventEmitter<boolean>();
didVote = false;
vote(agreed: boolean) {
this.voted.emit(agreed);
this.didVote = true;
}
template:`
<app-voter
*ngFor="let voter of voters"
[name]="voter"
(voted)="onVoted($event)"> //事件属性
</app-voter>`
export class VoteTakerComponent {
agreed = 0;
disagreed = 0;
voters = ['Dr IQ', 'Celeritas', 'Bombasto'];
onVoted(agreed: boolean) {
if (agreed) {
this.agreed++;
} else {
this.disagreed++;
}
}
2.3.5 父子组件通过本地变量互动
在父组件模板中新建一个本地变量待变子组件,利用这个变量来读取子组件的属性和调用子组件的方法。
template: `
<h3>Countdown to Liftoff (via local variable)</h3>
<button type="button" (click)="timer.start()">Start</button>
<button type="button" (click)="timer.stop()">Stop</button>
<div class="seconds">{{timer.seconds}}</div>
//新建变量 timer
<app-countdown-timer #timer></app-countdown-timer>
`
只可以在父组件的模板中使用该局部变量
2.3.6 通过@ViewChild()来互动
当父组件的类需要依赖于子组件类,此时不能使用本地变量方法,此时可以将子组件作为ViewChild,注入到父组件中。
@ViewChild(CountdownTimerComponent)
private timerComponent!: CountdownTimerComponent;
seconds() { return 0; }
ngAfterViewInit() {
setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
}
start() { this.timerComponent.start(); }
stop() { this.timerComponent.stop(); }
将子组件的视图插入到父组件类需要做一点额外的操作 必须导入对装饰器Viewchild 以及生命周期钩子AfterViewInit 的引用,通过@ViewChild() 装饰器,将子组件CountdownTimerComponent 注入到私有属性timerComponent 里面。
2.3.7 父子组件通过服务通讯
父子组件共享一个服务,利用该服务在组件家族内部实现双向通讯。
@Injectable()
export class MissionService {
private missionAnnouncedSource = new Subject<string>();
private missionConfirmedSource = new Subject<string>();
missionAnnounced$ = this.missionAnnouncedSource.asObservable();
missionConfirmed$ = this.missionConfirmedSource.asObservable();
announceMission(mission: string) {
this.missionAnnouncedSource.next(mission);
}
confirmMission(astronaut: string) {
this.missionConfirmedSource.next(astronaut);
}
}
constructor(private missionService: MissionService) {
missionService.missionConfirmed$.subscribe(
astronaut => {
this.history.push(`${astronaut} confirmed the mission`);
});
}
announce() {
const mission = this.missions[this.nextMission++];
this.missionService.announceMission(mission);
this.history.push(`Mission "${mission}" announced`);
if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
}
子组件通过构造函数注入该服务,因为是子组件所以获取到的也是父组件的这个服务实例。 subscript 变量在子组件ngOnDestory() 钩子函数内被销毁(调用unsubscribe()) ,防止内存泄漏的保护措施,在父组件中不需要因为服务与父组件的生命周期相同。
2.4 组件样式
特殊的选择器::host、:host-context :host 每个组件都会关联一个与组件选择器相匹配的元素,该元素被称为宿主元素,模板会渲染到其中。:host 伪类选择器可用于创建针对宿主元素自身的样式,而不是针对宿主内部的哪些元素。 host-context 在当前组件宿主元素的祖先节点上查找CSS类,直到文档的根节点为止。只能与其他选择器组合使用。
2.5 父子指令及组件间共享数据
父组件和一个或多个子组件共享数据,使用@Input() 和@Output() 。 @Input() :允许父组件更新子组件中的数据 @Output() :允许子组件向父组件发送数据。
2.6 内容投影
2.6.1 单插槽内容投影
创建一个组件,在其中投影一个组件。 在组件模板中添加,<ng-content> ,元素希望投影的内容出现在其中。 栗子:
template: `
<h2>Single-slot content projection</h2>
<ng-content></ng-content>
`
<app-zippy-basic>
<p>Is content projection cool?</p>
</app-zippy-basic>
元素是一个占位符,它不会创建真正的DOM元素。的自定义属性被忽略。
2.6.2 多插槽内容投影
一个组件可以具有多个插槽,每一个插槽可以指定一个CSS选择器,选择器可以指定哪些内容放入该插槽。 必须指定投影出现的位置,通过使用和select 属性来完成此任务。 栗子:
template: `
<h2>Multi-slot content projection</h2>
Default:
<ng-content></ng-content>
Question:
<ng-content select="[question]"></ng-content>
`
<app-zippy-multislot>
<p question>
Is content projection cool?
</p>
<p>Let's learn about content projection!</p>
</app-zippy-multislot>
question 属性的将内容投影到带有select=[question] 属性的 <ng-content> 元素。 如果组件包含不带select属性的<ng-content> 元素,则该实例将接受所有与其他<ng-content> 元素都不匹配的投影组件。
2.6.3 有条件的内容投影
后补
2.7 动态组件
当组件数量不多时可以使用ngif来判断component 栗子:
<ng-container *ngIf="radioValue === 'A'">
<app-component-a></app-component-a>
</ng-container>
<ng-container *ngIf="radioValue === 'B'">
<app-component-b></app-component-b>
</ng-container>
<ng-container *ngIf="radioValue === 'C'">
<app-component-c></app-component-c>
</ng-container>
当选项很多时,或者选项通过后端决定等复杂情况下,再用ngif实现会很复杂,代码不容易维护此时需要动态组件,动态的方式来生成或删除component 可以通过ViewContainerRef可以将一个或多个视图附着到组件中的容器,也只有它才可以加载组件。不过可以将新的组件作为其兄弟(节点)的DOM元素(容器),是兄弟不是父子。 步骤:
- 创建一个指令
ng g d directiveName
@Directive({
selector: '[appDynamic]'
})
export class DynamicDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
- 创建容器
通过单选框来选择动态加载组件 锚点设置在ng-template上, appDynamic (使用指令)
template:
`
<nz-radio-group [(ngModel)]="radioValue" (ngModelChange)="radioValueChange()">
<label nz-radio nzValue="A">A</label>
<label nz-radio nzValue="B">B</label>
<label nz-radio nzValue="C">C</label>
</nz-radio-group>
<ng-template appDynamic></ng-template>
`
class类:
componentList = {
componentA: ComponentAComponent,
componentB: ComponentBComponent,
componentC: ComponentCComponent,
}
@ViewChild(DynamicDirective) componentHost!:DynamicDirective;
radioValue: string = 'A';
radioValueChange() {
const viewContainerRef = this.componentHost.viewContainerRef;
viewContainerRef.clear();
viewContainerRef.createComponent(this.getComponent(this.radioValue));
}
getComponent(radioValue: string): any {
if (radioValue === 'A') {
return this.componentList['componentA']
} else if (radioValue === 'B') {
return this.componentList['componentB']
} else {
return this.componentList['componentC']
}
}
2.8 Angular元素(Element)
Angular元素就是打包成自定义元素的Angular组件
3 模板
Angular使用额外的特性扩展了模板中的HTML语法,Angular模板只是UI的一个片段,因此不包含<html> 、<body> 、<base> 等元素。并且不支持模板中使用<script> 元素
3.1 文本插值
**插值:**将表达式嵌入到被标记的文本中。默认情况下使用双花括号{{}} 作为定界符。
3.2 模板语句
模板语句是可在HTML中用于响应用户事件的方法或属性。
<button type="button" (click)="deleteHero()">Delete hero</button>
**语法:**与模板表达式一样,模板语句使用类似与JS的语言,支持赋值和带分号的串联表达式,不允许使用
3.3 绑定
3.3.1 属性绑定
类似与Property绑定,但是不能直接在方括号之间放置元素的Property,而是在Attribute 名前加上前缀attr ,后跟. ,然后,使用解析为字符串的表达式设置Attribute 值。
<p [attr.attribute-you-are-targeting]="expression"></p>
当表达式解析为null 或undefined 时,Angular会完全删除该Attribute。 ARIA Attribute
<button type="button" [attr.aria-label]="actionName">{{actionName}} with Aria</button>
绑定colSpan colspan帮助以编程方式让表格保持动态 将[attr.colspan]设置为等于某个表达式。
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
3.3.2 类和样式绑定
使用类和样式绑定从元素的class属性中添加和删除css类名,以及动态设置样式。 绑定到单个CSS class [class.sale]='onSale' sale为类名 当表达时onSale为真值时,Angular会添加类,为假时会删除类,undefined除外 绑定到多个CSS class类 [class]='classExpression' calssExpression可以为:
- 用空格分隔的类名字符串
"my-class-1 my-class-2 my-class-3" - 以类名作为键名并将真或假表达式作为值的对象。
{foo: true, bar: false} - 类名的数组
['foo', 'bar'] 绑定到单一样式 创建单个样式绑定以style 前缀后跟点加CSS样式的名称。
<nav [style.background-color]="expression"></nav>
<nav [style.backgroundColor]="expression"></nav>
绑定到多个样式
<nav [style]='navStyle'>
navStyle = 'font-size: 1.2rem; color: cornflowerblue;';
3.3.3 事件绑定
<button (click)="onSave()">Save</button> click :事件名 onSave() :模板语句
3.3.4 属性绑定
属性绑定在单一方向上将值从组件的属性送到目标元素的属性。 绑定到属性:要绑定到元素的属性,放到方括号内,将此属性标识为目标属性。 <img alt="item" [src]="itemImageUrl"> colspan 和 colSpan 最容易混淆的地方是 colspan 这个 Attribute 和 colSpan 这个 Property。请注意,这两个名称只有一个字母的大小写不同。 <tr><td [colSpan]="1 + 1">Three-Four</td></tr>
3.3.5 双向绑定
双向绑定将属性和事件绑定结合在一起 Angular 的双向绑定语法是方括号和圆括号的组合 [()]。[] 进行属性绑定,() 进行事件绑定 <app-sizer [(size)]="fontSizePx"></app-sizer> **原理:**属性绑定展示数据,事件绑定修改数据
3.4 管道
管道用于转换数据进行显示。 内置管道:
uppercase : 转换成大写字符 {{ str | uppercase }}
lowercase : 转换成小写 {{ str | lowercase }}
date : 日期格式转换 {{ today | date: ‘yyyy-MM-dd HH:mm:ss’ }}
number : 数字转换 {{ p | number:‘1.2-4’ }} 【格式:{最少整数位数}.{最少小数位数}-{最多小数位数},即保留2~4位小数】
slice : 字符串截取 {{ ‘hello world!’ | slice:0:3 }} //输出结果:“hel” 管道的优先级比三目运算符高 **使用参数和管道链来格式化数据:**模板表达式 {{ amount | currency:'EUR' }} 会把 amount 转换成欧元。紧跟在管道名称(currency)后面的是冒号(:)和参数值(‘EUR’)。多个参数使用:分割开来 {{ amount | currency:'EUR':'Euros '}} 管道串联:{{ birthday | date | uppercase}} 自定义管道 创建指令: ng g p pipeName
3.5 模板变量
模板变量事项在模板的另一部分使用这个部分的数据。 可以引用的东西:
- 模板中的DOM元素
- 指令或组件
- 来自ng-template的TemplateRef
- Web组件
**语法:**使用井号#来声明一个模板变量,可以在组件模板中的任何地方引用某个模板变量。
<input #phone placeholder="phone number" />
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button type="button" (click)="callPhone(phone.value)">Call</button>
模板变量的赋值:
- 如果在组件上声明变量,该变量就会引用该组件实例。
- 如果在标准的HTML标记上声明变量,该变量就会引用该元素。
- 如果在
<ng-template> 元素上声明变量,该变量就会引用一个TemplateRef 实例来代表此模板。 ***TemplateRef:***表示一个内嵌模板,可用于实例化内嵌的视图。要想根据模板实例化内嵌的视图,可以使用ViewContainerRef 的createEmbeddedView() 方法。 **作用域:**模板变量的范围为声明它们的模板。 同样,诸如 *ngIf 和 *ngFor 类的结构指令或 <ng-template> 声明会创建一个新的嵌套模板范围,就像 JavaScript 的控制流语句(例如 if 和 for 创建新的词法范围。你不能从边界外访问这些结构指令之一中的模板变量。 **在嵌套模板中访问:**内部模板可以访问外模板定义的模板变量。
<input #ref1 type="text" [(ngModel)]="firstExample" />
<span *ngIf="true">Value: {{ ref1.value }}</span>
<span> 上的 *ngIf 会创建一个新的模板范围,其中包括其父范围中的 ref1 变量。 外部的父模板访问子范围中的变量不行。 模板输入变量 是一个具有在创建该模板实例时设置的值的变量
<ul>
<ng-template ngFor let-hero let-i="index" [ngForOf]="heroes">
<li>Hero number {{i}}: {{hero.name}}
</ng-template>
</ul>
实例化<ng-template> 时,可以传递国歌命名值,这些值可以绑定到不同的模板输入变量。输入变量的let- 声明的右侧可以指定应该用于该变量的值。
4. 指令
指令:为元素添加额外的行为的类。使用指令可以管理表单、列表、样式以及可以让用户看到的内容 不同类型的指令:
- 组件:带有模板的指令。
- 属性型指令:更改元素、组件或其他指令的外观或行为的指令。
- 结构型指令:通过添加和删除DOM元素来改变DOM布局。
4.1 内置指令
属性型指令会监听并修改其他HTML元素和组件的行为、Attribute(属性)和Property(特性)
4.1.1 内置属性指令
通用指令:
NgClass:添加和删除一组CSS类 NgStyle:添加和删除一组HTML样式。 NgModel:将双向数据绑定添加到HTML表单元素 内置指令只会使用公开API。不会访问任何无法被其他指令访问的私有API。
NgClass
NgClass:同时添加或删除多个CSS类。 <div [ngClass]="isSpecial ? 'special' : ''">This div is special</div> 如果时添加或删除单个类,可以使用类绑定 。 类绑定:[class.sale]="onSale" 与方法一起使用: 通过一个对象来设置属性currentClasses ,该对象每个键(key)都是一个CSS类名。如果键为true,则ngClass添加该类。如果为false则删除该类。
currentClasses: Record<string, boolean> = {};
setCurrentClasses() {
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial
};
}
使用: <div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special.</div>
NgSyle
NgStyle向组件添加的都是内联样式。 栗子:
currentStyles: Record<string, string> = {};
setCurrentStyles() {
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px'
};
}
<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
4.1.3 内置结构型指令
结构型指令的职责是HTML布局,塑造或重塑DOM的结构,通过添加、移除和操作它们附加的宿主元素来实现。
NgIf
用于宿主元素添加或删除元素 为false则从DOM中移除一个元素及其后代。然后Angular销毁其组件,释放内存和资源。 isActive 为true添加组件到DOM中 <app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>
NgFor条目列表
Ngif与NgFor不可同时在同一个元素上。 用*ngFor的tackBy跟踪条目: 通过跟踪对条目列表的更改,可以减少应用程序对服务器的调用次数。使用*ngFor的trackBy属性,Angular只能更改和重新渲染已更改的条目,而不必重新加载整个条目列表。
trackByItems(index: number, item: Item): number { return item.id; }
<div *ngFor="let item of items; trackBy: trackByItems">
({{item.id}}) {{item.name}}
</div>
4.1.4 为没有DOM元素的指令安排宿主
<ng-container> 是一个分组元素,不会干扰样式或布局,因为Angular不会将其放置在DOM中。当没有单个元素承载指令时可以使用<ng-container> 。
<ng-container *ngIf="hero">
and saw {{hero.name}}. I waved
</ng-container>
<select [(ngModel)]="hero">
<ng-container *ngFor="let h of heroes">
<ng-container *ngIf="showSad || h.emotion !== 'sad'">
<option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
</ng-container>
</ng-container>
</select>
4.1.5 NgSwitch
NgSwitch 会根据切换条件显示几个可能的元素中的一个。 NgSwitch 是一组指令
NgSwitch :一个属性型指令,会改变其伴生指令的行为NgSwitchCase :当其绑定值等于开关值时将其元素添加到DOM中,而在其不等于开关值时将其绑定之移除。NgSwitchDefault :当没有选中的NgSwitchCase 时,将其宿主元素添加到DOM中。
<div [ngSwitch]="currentItem.feature">
<app-stout-item *ngSwitchCase="'stout'" [item]="currentItem"></app-stout-item>
<app-device-item *ngSwitchCase="'slim'" [item]="currentItem"></app-device-item>
<app-lost-item *ngSwitchCase="'vintage'" [item]="currentItem"></app-lost-item>
<app-best-item *ngSwitchCase="'bright'" [item]="currentItem"></app-best-item>
<!-- . . . -->
<app-unknown-item *ngSwitchDefault [item]="currentItem"></app-unknown-item>
</div>
4.2 属性型指令
属性型指令,可以改变DOM元素和Angular组件的外观或行为。 创建指令:ng generate directive highlight @Directive() 装饰器的配置属性会指定指令的 CSS 属性选择器 [appHighlight]。 从@angular/core 导入ElementRef 。ElementRef的nativeElement属性会提供对宿主DOM元素的直接访问权限。 在指令的 constructor() 中添加 ElementRef 以注入对宿主 DOM 元素的引用,该元素就是 appHighlight 的作用目标。 向 HighlightDirective 类中添加逻辑,将背景设置为黄色。
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
}
<p appHighlight>Highlight me!</p>
4.2.1 处理用户事件
要添加事件处理程序,每个事件处理程序都需要带有@HostListener() 装饰器。
|