在上一章中介绍了 attribute directive 的用法,接着要来介绍另一种 Angular 中内建的 directive 那就是 sturctural directive。
它的用途主要是用来改变 DOM 的结构
,他会塑造或重构 DOM 的结构,通常是添加、删除和操作他所附加的 element,本章中将会介绍介绍最常见的三种 structural directive :
可以利用 NgIf directive 为宿主 element 添加或删除 element,简单来说当 NgIf 为 false 时从 DOM 中移除一个 element 与他的所有子层并且将它所使用到的所有 component 从内存中释放,反之会新增一个的 element,来举一个简单的例子,当我们点击画面中的按钮时会将 child component 显示出来。
在 app.component.ts 中新增一个 boolean 的 property 用於决定是否显示 child.component,与一个 method 用来改变这个 property 的状态
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
isActive = false;
onClick() {
this.isActive = !this.isActive;
}
}
在 app.component.html 中引用 child.component 的 selector 并使用 *ngIf
来控制是否显示他
<!-- app.component.html -->
<app-child *ngIf="isActive"></app-child>
<button (click)="onClick()">{{ isActive ? 'not display' : 'display' }}</button>
在画面中可以看到当我们点击了画面中的按钮时,会更改 isActive 的状态,如果 *ngIf 为 true 的话则会将 child.component 显示出来,反之会在 DOM 中将他移除掉。
而要注意如果要使用 structural directive 需要在前面都加上米字号(*)
作为前缀字喔!
既然提到了 if 不免俗的也要介绍 else 毕竟他们通常是一组出现的,他与 javascript 的规则一样,当 不满足 if 中的表达式时就会进到 else 中,举个例子吧
保持上一个例子的 app.component.ts 内容
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
isActive = false;
onClick() {
this.isActive = !this.isActive;
}
}
要把 app.component.ts 中的内容稍微改一下
<!-- app.component.html -->
<app-child-1 *ngIf="isActive; else otherTemplate"></app-child-1>
<ng-template #otherTemplate>
<app-child-2></app-child-2>
</ng-template>
<button (click)="onClick()">{{ isActive ? 'not display' : 'display' }}</button>
在画面中可以看到,当我们更改了 isActive 的状态时,在画面中所呈现的 component 也会不一样,还记得在 template variable 中提到的 哈希符号 #
吗,这边可以把它看成当 *ngIf 为 true 时会显示 <app-child-1>
这个 component,而当为 false 时就会显示 <ng-template>
所包住的 <app-child-2>
,记得当要使用 NgIfElse
时需要在绑定的 property 或表达式後面加上分号(;)
喔!
在前几天我们介绍 Text interpolation 时就有使用到 NgFor 的技巧,他就是利用迭代的方式将 component 中 arr 型态的 property 的内容一一呈现在画面中。
<div *ngFor="let item of items">{{item.name}}</div>
在 *ngFor 後面的字串表达式 let item of items
, Angular 会对他做以下的处理:
本地项目循环的变量(使用 let 宣告个变量)
中let item of items
转换成围绕宿主 element 的 了解了 *ngFor 与用法後,这边要介绍一些在使用 *ngFor 时特别好用的技巧,其实在使用 *ngFor 时他不只会将阵列中的值存在本地变量中,你还可以储存别的东西到别的变量中:
可能你会想说,啊这些有什麽用? 这可是非常好用的啊!
当你在开发专案时,一个列表中可能会需要在他的最後一行加上一个 +
的符号用於新增内容,或对偶数的 item 显示不同颜色等等,这边来搭配着前几天讲的内容做一个小小的例子:
在 app.component.ts 中新增一个 Heros 里面装着所有英雄的名称,与两个 method
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
isShowAddHero!: boolean; // (1)
Heros = [ // (2)
{ id: 11, name: 'Dr Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' },
];
onOpenAddHero() { // (3)
this.isShowAddHero = true;
}
onAddHero(heroName: string) { // (4)
this.Heros.push({ id: Math.random(), name: heroName })
this.isShowAddHero = false;
}
}
在 app.component.html 中新增内容
<!-- app.component.html -->
<h2 class="title">Hero List</h2>
<div class="heroList" *ngFor="let hero of Heros; last as isLast; odd as isOdd">
<div class="heroItem" [ngClass]="isOdd ? 'oddClass' : ''">
{{hero.name}}
<span *ngIf="isLast">
<button (click)="onOpenAddHero()" class="openBtn">+</button>
</span>
</div>
</div>
<div *ngIf="isShowAddHero" class="addContent">
<input type="text" #hero>
<button (click)="onAddHero(hero.value)">Add hero</button>
</div>
这边我们详细的介绍一下,你会看到很多以前都介绍过的内容:
last as isLast
来获得最後一项odd as isOdd
来获得偶数项isOdd
决定是否要多绑定一个 CSS class*ngIf
判断是否为最後一项,用於决定是否显示新增英雄的按钮<button>+</button>
中使用 event binding 绑定 component 中的 method<div>
利用 *ngIf 决定是否要显示在画面上<input>
中设置为 template variable 让其他地方的 element 获得他的数据<button>Add hero</button>
中使用 event binding 绑定 component 中的 method 并从 <input>
中透过 template variable 拿到数据做为参数在画面中可以看到,列表中的偶数部分因为符合 [ngClass]="isOdd ? 'oddClass' : ''"
所以多添加了一个变成蓝色的 CSS class,而最下面的 item 因为符合 *ngIf="isLast"
所以只有他有 + 的按钮,而当点击了+後符合 *ngIf="isShowAddHero"
所以会打开新增 hero 的介面,当按下新增 hero 的介面的按钮後将输入在 <input>
的内容 push 近英雄表单中,而+会一直保持出现在最後一个 item 的右边。
这个例子用了很多之前介绍的技巧,如果有看不懂的建议回去复习一下,真的有问题的话有欢迎在下方留言喔!
在 Angular 中有一个特别的 HTML element,他就是 <ng-container>
,之所以会说他特别是因为他是一个不会干扰样式或布局的分组元素,因为 Angular 不会将他放进 DOM 里面,所以当没有单个 element 可以乘载 directive 时,就可以使用这个特别的 <ng-container>
,通常都会将它搭配 structural directive
使用,举个例子吧
<p>
I turned the corner
<ng-container *ngIf="hero">
and saw {{hero.name}}. I waved
</ng-container>
and continued on my way.
</p>
上面例子中可以看到,我们希望在 <p>
的段落中透过使用 *ngIf
决定是否要显示英雄名称,如果这时候我们使用其他的 HTML element 来乘载 *ngIf
的话,会造成画面样式跟布局的错误,所以就可以使用 <ng-container>
,因为他不会被放进 DOM 里面,作为乘载 structural directive 再好不过。
提到了 <ng-container>
不免俗的要来介绍一下他跟 <ng-template>
的差别:
<ng-template>
: 是用於呈现 HTML 的 Angular element,他不会直接显示在画面上,需要透过 structural directive 控制它是否要显示在画面中,如果没有被显示在画面中时会以注释
的形式呈现在 DOM 中,比较常会用在写结构性变化的时候,比如
<div *ngIf="show; else notshow">
当 show = true 时,显示这些内容
</div>
<ng-template #notshow>
当 show = false 时,显示这些内容
</ng-template>
<ng-container>
: 他是 Angular 解析器识别的语法 element,他不是 directive, component, class 或 interface,他更像是 javascript 的花括号,常被用在不想要多写不必要的 HTML element 时但又想将一个区块的 HTML 包起来处理,就会使用到 <ng-container>
常用来做 structural directive 的载体。
<p>
I turned the corner
<ng-container *ngIf="hero">
and saw {{hero.name}}. I waved
</ng-container>
and continued on my way.
</p>
最後要来介绍最後一种常见的 structural directive 那就是 NgSwitch
,他其实与 Javascript 的 switch case 一样,NgSwitch 会根据 switch 跳健从几个可能的元素中显示其中一个,Angular 只会将符合条件的元素放进 DOM 中,而 NgSwitch 是由三种 directive 组成的:
一样举个例子
首先先在 app.component.ts 中新增一个 property 与 method,用於改变 property 状态与接收 event
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
showArea = 0;
onClick(number: number) {
switch (number) {
case 1:
this.showArea = 1;
break;
case 2:
this.showArea = 2;
break;
case 3:
this.showArea = 3;
break;
default:
this.showArea = 0;
break;
}
}
}
在 app.component.html 中新增三个 <button>
用来让使用者点击要显示哪一个内容,并在下方使用 NgSwitch 来判断使用者的选项
<!-- app.component.html -->
<ul>
<li>
<label>Display number-1 area: </label>
<button (click)="onClick(1)">Area 1</button>
</li>
<li>
<label>Display number-2 area: </label>
<button (click)="onClick(2)">Area 2</button>
</li>
<li>
<label>Display number-3 area: </label>
<button (click)="onClick(3)">Area 3</button>
</li>
<li>
<label>Display default area: </label>
<button (click)="onClick(99)">default</button>
</li>
</ul>
<div [ngSwitch]="showArea">
<div *ngSwitchCase="1">
<h2>This is area 1</h2>
<p>Hello world</p>
</div>
<div *ngSwitchCase="2">
<h2>This is area 2</h2>
<p>Learning Angular</p>
</div>
<div *ngSwitchCase="3">
<h2>This is area 3</h2>
<p>Know how to use ngSwitch</p>
</div>
<div *ngSwitchDefault>
<h2>This is default area</h2>
<p>Please select a checkbox</p>
</div>
</div>
在画面中可以看到当我们点击画面中的不同按钮时,会改变 showArea 的值,进而改变下方显示的区域内容,我特意在 html 与 ts 档中都有使用 switch case 的语法,可以互相比对一下他们的用法,基本上都是差不多的,所以等於说只要会 javascript 的 switch case 语法就同时也学会了 ngSwitch!
本章中介绍了常见的 structural directive 与他们各自的用法,其实 structural directive 的使用方法基本上都跟 Javascript 类似,所以等於说之前知道 Javascript 的 if else
, for loop
, switch case
的用法的话这一章会学得非常快,不过要注意的是 <ng-container>
与 <ng-template>
的差别与使用场景,除此之外其他的应该都相对简单。
虽然本章介绍了常见的内建 structural directive,不过再开发专案时一定会遇到内建 structural directive 无法处理的问题,这时候就跟 pipe 一样需要客制化了,下一章将会介绍如何客制化自己的 attribute directive 用於处理内建 attribute directive 无法处理的问题,那我们就明天见吧
<<: [Day06] swift & kotlin 入门篇!(4) 基础语法-转型与合并使用
>>: Day2:AWS Shared Responsibility Model
我认为想要做电商的新手,必须要掌握以下几点: 1. 确定产品和货源 成立电商第一步就是要确认自己所要...
D27. 跳脱字元 跳脱字元指的是脱离原字元的意思,例如 " 原本在C++中是用来当作字串...
工作上遇到了些问题纪录一下 因作业关系,修改了服务中的设定档 服务启动发生错误: 错误14001 使...
前情提要 前一篇带各位实作了爬取 Ubuntu ISO 映像档的爬虫,并存在 JSON 档。 开始之...
大家好,我是长风青云。今天是铁人赛第四天。 今天我真的有点赶,我觉得我以後还是不要当天再开始好了。 ...