创建了一个 “重学TypeScript” 的微信群,想加群的小伙伴,加我微信 “semlinker”,备注 “1” 。阿里、京东、腾讯的大佬都在群里等你哟。
semlinker/awesome-typescript 1.6K
目录
- 第一节 - 创建指令
- 第二节 - 定义输入属性
- 第三节 - 事件处理
- 第四节 - 获取宿主元素属性值
- 第五节 - 使用
<ng-template>
元素 - 第六节 - 使用
ngTemplateOutlet
指令 - 第七节 - 使用
ngComponentOutlet
指令 - 第八节 - 创建结构指令
- 第九节 - 微语法简介
阅读须知
本系列教程的开发环境及开发语言:
基础知识
Angular CLI 基本使用
1
| $ npm install -g @angular/cli
|
1 2
| $ cd project-name $ ng serve
|
Angular 指令简介
Angular 的指令分为三种:
- 组件(Component):用于构建UI组件,继承于 Directive 类
- 属性指令(Attribute Directive):用于改变组件的外观或行为
- 结构指令(Structural Directive):用于动态添加或删除
DOM
元素来改变 DOM
布局
Angular 组件组成图

(图片来源于网络)
第一节 - 创建指令
在 Angular 中,我们可以使用 HostBinding
装饰器,实现元素的属性绑定。
指令的作用
该指令用于演示如何利用 HostBinding
装饰器,设置元素的 innerText
属性。
指令的实现
1 2 3 4 5 6 7 8
| import { Directive, HostBinding} from '@angular/core';
@Directive({ selector: '[greet]' }) export class GreetDirective { @HostBinding() innerText = 'Hello, Everyone!'; }
|
指令的应用
1 2 3 4 5 6 7 8 9 10
| import { Component } from '@angular/core';
@Component({ selector: 'app-root', template: ` <h2>Hello, Angular</h2> <h2 greet>Hello, Angular</h2> `, }) export class AppComponent { }
|
第二节 - 定义输入属性
为了能够让用户自定义 GreetDirective
指令的问候内容,我们需要使用 Input
装饰器去定义指令的输入属性。
指令的作用
该指令用于演示如何利用 Input
装饰器,定义指令的输入属性,从而实现让用户自定义问候内容。
指令的实现
1 2 3 4 5 6 7 8 9 10 11
| import { Directive, HostBinding, Input } from '@angular/core';
@Directive({ selector: '[greet]' }) export class GreetDirective { @Input() greet: string; @HostBinding() get innerText() { return this.greet; } }
|
指令的应用
1 2 3 4 5 6 7 8 9 10
| import { Component } from '@angular/core';
@Component({ selector: 'app-root', template: ` <h2>Hello, Angular</h2> <h2 [greet]="'Hello, Semlinker!'">Hello, Angular</h2> `, }) export class AppComponent { }
|
第三节 - 事件处理
在 Angular 中,我们可以使用 HostListener
属性装饰器,实现元素的事件绑定。
指令的作用
该指令用于演示如何利用 HostListener
装饰器,监听用户的点击事件。
指令的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { Directive, HostBinding, HostListener, Input } from '@angular/core';
@Directive({ selector: '[greet]' }) export class GreetDirective { @Input() greet: string;
@HostBinding() get innerText() { return this.greet; }
@HostListener('click',['$event']) onClick(event) { this.greet = 'Clicked!'; } }
|
指令的应用
1 2 3 4 5 6 7 8 9 10
| import { Component } from '@angular/core';
@Component({ selector: 'app-root', template: ` <h2>Hello, Angular</h2> <h2 [greet]="'Hello, Semlinker!'">Hello, Angular</h2> `, }) export class AppComponent { }
|
第四节 - 获取宿主元素属性值
在 Angular 中,我们可以通过 Attribute
装饰器来获取指令宿主元素的属性值。
指令的作用
该指令用于演示如何利用 Attribute
装饰器,获取指令宿主元素上的自定义属性 author
的值。
指令的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { Directive, HostBinding, HostListener, Input, Attribute } from '@angular/core';
@Directive({ selector: '[greet]' }) export class GreetDirective { @Input() greet: string;
@HostBinding() get innerText() { return this.greet; }
@HostListener('click',['$event']) onClick(event) { this.greet = 'Clicked!'; console.dir(event); }
constructor(@Attribute('author') public author: string) { console.log(author); } }
|
指令的应用
1 2 3 4 5 6 7 8 9 10 11
| import { Component } from '@angular/core';
@Component({ selector: 'app-root', template: ` <h2>Hello, Angular</h2> <h2 [greet]="'Hello, Semlinker!'" author="semlinker">Hello, Angular</h2> `, }) export class AppComponent { }
|
第五节 - 使用 <ng-template>
元素
在 Angular 中,我们可以通过 ViewChild
装饰器来获取视图中定义的模板元素,然后利用 ViewContainerRef
对象的 createEmbeddedView()
方法,创建内嵌视图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { Component, TemplateRef, ViewContainerRef, ViewChild, AfterViewInit } from '@angular/core';
@Component({ selector: 'app-root', template: ` <ng-template #tpl> Hello, Semlinker! </ng-template> `, }) export class AppComponent implements AfterViewInit{ @ViewChild('tpl') tplRef: TemplateRef<any>;
constructor(private vcRef: ViewContainerRef) {}
ngAfterViewInit() { this.vcRef.createEmbeddedView(this.tplRef); } }
|
第六节 - 使用 ngTemplateOutlet
指令
ngTemplateOutlet 的作用
该指令用于基于已有的 TemplateRef
对象,插入对应的内嵌视图。在应用 NgTemplateOutlet 指令时,我们可以通过 [ngTemplateOutletContext]
属性来设置 EmbeddedViewRef
的上下文对象。绑定的上下文应该是一个对象,此外可通过 let
语法来声明绑定上下文对象属性名。
ngTemplateOutlet 的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { Component } from '@angular/core';
@Component({ selector: 'app-root', template: ` <ng-template #stpl> Hello, Semlinker! </ng-template> <ng-template #atpl> Hello, Angular! </ng-template> <div [ngTemplateOutlet]="atpl"></div> <div [ngTemplateOutlet]="stpl"></div> `, }) export class AppComponent { }
|
ngOutletContext 的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { Component } from '@angular/core';
@Component({ selector: 'app-root', template: ` <ng-template #stpl let-message="message"> <p>{{message}}</p> </ng-template> <ng-template #atpl let-msg="message"> <p>{{msg}}</p> </ng-template> <ng-template #otpl let-msg> <p>{{msg}}</p> </ng-template> <div [ngTemplateOutlet]="atpl" [ngTemplateOutletContext]="context"> </div> <div [ngTemplateOutlet]="stpl" [ngTemplateOutletContext]="context"> </div> <div [ngTemplateOutlet]="otpl" [ngTemplateOutletContext]="context"> </div> `, }) export class AppComponent { context = { message: 'Hello ngOutletContext!', $implicit: 'Hello, Semlinker!' }; }
|
第七节 - 使用 ngComponentOutlet
指令
ngComponentOutlet 的作用
该指令用于使用声明式的语法,动态加载组件。
简单语法
1
| <ng-container *ngComponentOutlet="componentTypeExpression"></ng-container>
|
完整语法
1 2 3 4
| <ng-container *ngComponentOutlet="componentTypeExpression; injector: injectorExpression; content: contentNodesExpression;"> </ng-container>
|
ngComponentOutlet 的使用
app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @NgModule({ declarations: [ AppComponent, SignUpComponent, AlertSuccessComponent, AlertDangerComponent ], entryComponents: [ AlertSuccessComponent, AlertDangerComponent ], bootstrap: [AppComponent] }) export class AppModule { }
|
app.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import { Component } from '@angular/core';
@Component({ selector: 'alert-success', template: ` <p>Alert success</p> `, }) export class AlertSuccessComponent { }
@Component({ selector: 'alert-danger', template: ` <p>Alert danger</p> `, }) export class AlertDangerComponent { }
@Component({ selector: 'my-app', template: ` <h1>Angular version 6</h1> <ng-container *ngComponentOutlet="alert"></ng-container> <button (click)="changeComponent()">Change component</button> `, }) export class AppComponent { alert = AlertSuccessComponent; changeComponent() { this.alert = AlertDangerComponent; } }
|
第八节 - 创建结构指令
指令的功能
该指令实现 ngIf
指令相反的效果,当指令的输入条件为 Falsy
值时,显示DOM元素。
指令的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[exeUnless]' }) export class UnlessDirective {
@Input('exeUnless') set condition(newCondition: boolean) { if (!newCondition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } }
constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { } }
|
指令的应用
1 2 3 4 5 6 7 8 9 10 11
| import { Component } from '@angular/core';
@Component({ selector: 'app-root', template: ` <h2 *exeUnless="condition">Hello, Semlinker!</h2> `, }) export class AppComponent { condition: boolean = false; }
|
第九节 - 微语法简介
Angular 微语法能让你通过简短的、友好的字符串来配置一个指令。微语法解析器把这个字符串转换成 <ng-template>
上的属性。
我们以 ngFor
指令为例:
1
| <li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>
|
经过微语法解析器解析后,将生成以下模板:
1 2 3
| <ng-template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackByFn"> <li>...</li> </ng-template>
|
解析的过程如下:
- ngFor + (of -> Of) -> ngForOf
- ngFor + (trackBy -> TrackBy) -> ngForTrackBy
- let 关键字声明一个模板输入变量,示例中的输入变量是 item 和 i。
let item
和 index as i
会被转换为 let-item
和 let-i="index"
- ngFor 指令在列表上循环,每个循环中都会设置和重置它自己上下文对象上的属性。这些属性包括
index
和一个特殊的属性名 $implicit
(隐式变量) - let-i 变量是通过
let-i="index"
来定义的。Angular 把它们设置为上下文对象中的 index
属性的当前值。 let-item
并没有指定其上下文属性。它的来源是隐式的。Angular 将 let-item
设置为此上下文 $implicit
属性的值。
了解上述的语法,我们就可以灵活地定义属性自己的结构指令。最后我们来看一下 ngFor
指令的定义:
1 2
| @Directive({selector: '[ngFor][ngForOf]'}) export class NgForOf<T> implements DoCheck {}
|
如果想详细了解微语法,请移步 Angular 官方文档。
欢迎小伙伴们订阅全栈修仙之路,及时阅读 TypeScript、Node/Deno、Angular 技术栈最新文章。