元件 (Component) 封裝了頁面檢視中的區域,其包含了頁面、樣式與邏輯;除此之外,Angular 還提供了指令 (Directive),讓 Angular 在渲染頁面時,根據指令內的處理邏輯對 DOM 進行轉換。
指令 (Directive) 包含了屬性型指令 (Attribute Directive) 與結構型指令 (Structural Directive) 兩種,前者用於改變 DOM 元素的外觀與行為,後者則是操控 DOM 樹,透過新增、移除或替換 DOM 元素來修改頁面結構。這一篇會著重在屬性型指令 (Attribute Directive) ,且將會自訂一個指令 (Directive) 來實作待辦事項狀態的文字顏色變更。
ngClass
實作狀態文字顏色變更在先前文章中,利用了樣式繫結 (Style Binding) 與類別繫結 (Class Binding) 實作的狀態文字顏色變更,也可以利用 Angular 內建的 ngStyle
與 ngClass
等屬性型指令進行實作。在 task.component.ts 檔案中,宣告 stateClass
屬性,並在 ngOnChanges
方法中依狀態設定其值。
export class TaskComponent implements OnInit, OnChanges {
stateClass: { [key: string]: boolean };
ngOnInit(): void {}
ngOnChanges(): void {
this.stateClass = {
doing: this.state === TaskState.Doing,
finish: this.state === TaskState.Finish,
};
this.stateDesc = this.getStateDesc();
}
}
接著,在 task.component.html 中使用 ngClass
指令,將其值繫結至 stateClass
屬性;此指令會依物料屬性的布林值來新增或刪除樣式類別。
<span [ngClass]="stateClass">{{ stateDesc }}</span>
然而,此一需求也可以透過自訂屬性型指令 (Attribute Directive) 來實作。
ng generate directive [名稱 或 路徑] [參數]
-- 或 --
ng g d [名稱 或 路徑] [參數]
由於指令 (Directive) 主要負責針對 DOM 進行所需要的轉換,所以只會有 TypeScript 檔案來負責轉換的處理邏輯,而不存在頁面與樣式。因此在 Angular CLI 建立指令的參數中,除了沒有頁面與樣式的設定,其餘與建立元件的參數相似。
接下來,就利用 ng g d task/task-state-color
命令,來建立 TaskStateColorDirective 元件來處理待辦事項狀態文字的顏色判斷。
在建立指令時,因
--flat
參數預設值為 true,故不會新增一層目錄。
如同元件 (Component),指令的使用方式,是依 @Directive
裝飾器所定義的 selector
參數內容。如下圖所示,預設上元件 (Component) 與指令 (Directive) 的選擇器命名規則是不同的,前者是單字與單字間使用連接符號 (kebab-case) 的命名方式,後者則是小駝峰式 (camelCase);如要調整可以修改 tslint.json 檔案內的 component-selector
與 directive-selector
屬性定義。
接下來,就自訂屬性型指令 (Attribute Directive) 來實作狀態文字顏色的變更,使得在 task.component.html 檔案中,可以修改成下面語法:
<span [appTaskStateColor]="state">{{ stateDesc }}</span>
要實作此需求,必須在指令 (Directive) 程式中取得宿主元素(在此即為 <span>
標籤),並且針對該宿主元素依傳入的狀態加入特定的樣式類別。因此在 TaskStateDirective 元件中,除了利用 @Input()
裝飾器定義接收狀態的輸入屬性外,還需要注入 ElementRef
與 Renderer2
,前者代表使用指令 (Directive) 的 DOM 元素,後者則可以針對 DOM 元素進行操作。
@Directive({
selector: "[appTaskStateColor]",
})
export class TaskStateColorDirective {
@Input("appTaskStateColor") state: TaskState;
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
}
最後,由於傳入的狀態資料是可變動的,因此需要實作 OnChanges
介面,並在 ngOnChanges()
方法中依待辦事項狀態,對宿主元件新增所對應的樣式類別名稱。
@Directive({
selector: "[appTaskStateColor]",
})
export class TaskStateColorDirective implements OnChanges {
@Input("appTaskStateColor") state: TaskState;
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
ngOnChanges(): void {
this.clearElementClass();
this.addClassByState();
}
private clearElementClass(): void {
const classNames = ["none", "doing", "finish"];
classNames.forEach((className) =>
this.renderer.removeClass(this.elRef.nativeElement, className)
);
}
private addClassByState(): void {
let className: string;
switch (this.state) {
case TaskState.None:
className = "none";
break;
case TaskState.Doing:
className = "doing";
break;
case TaskState.Finish:
className = "finish";
break;
}
this.renderer.addClass(this.elRef.nativeElement, className);
}
}
這一篇透過自訂屬性型指令 (Attribute Directive),封裝了狀態文字外觀的邏輯,實作的程式碼放在 GitHub 中。接下來則會利用結構型指令 (Structural Directive) ,實作待辦事項清單的頁面顯示。