https://www.youtube.com/watch?v=-8mGWhY4jGo&list=PL9LUW6O9WZqgUMHwDsKQf3prtqVvjGZ6S&index=9
S06 由 Kevin Yang 大大主講,藉由大大導讀文件後,我們就有能力能自己查文件了
看完基礎但內容有點多的 Component 之後,來學比較輕鬆的 Directive
[S06E04] Directive 片長只有 35 分鐘,重點在於跟著 Kevin Yang 老師學會怎麼看文件
首先,先開好片頭導讀的文章,再跟著影片一起看,以後才有能力自己查文件
今天內容有:(方便search)
跟著文件的範例操作一次
<p appHighlight>p tag裡面的文字</p> 掛在<TAG>上面
ng g directive highlight
import { Directive, ElementRef, AfterViewInit } from '@angular/core';
@Directive({
selector: '[appHighlight]' // selector 用 attribute 的方式
// selector: 'appHighlight' // 也可以用<TAG>的方式
})
export class HighlightDirective implements AfterViewInit {
constructor(el: ElementRef) { // 注入 ElementRef
// el 就是那個 <p appHighlight>Highlight me!</p>
el.nativeElement.style.backgroundColor = 'yellow';
// 由於傳入的 ElementRef 不一定是 HTML的Element,使用nativeElement來取可避免錯誤
console.log(el.nativeElement);
// HTMLParagraphElement {tagName:"p",attributes:{...},innerHTML:"p tag裡面的文字",...}
}
// 這裡值才初使化好
ngAfterViewInit(){
console.log( (el.nativeElement as HTMLParagraphElement).innerText );
// p tag裡面的文字
}
}
使用 @HostListener decorator 來訂閱 DOM element的事件
輕鬆的幫 <p appHighlight>p tag裡面的文字</p>
增加監聽mouse事件,做到更多互動
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
// 監聽mouse移進來
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
}
// 監聽mouse移出<p appHighlight></p>
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
// <p appHighlight>Highlight me!</p>
// <p [appHighlight]="color">Highlight me!</p> // 父元件傳color進去
@Input('appHighlight') highlightColor: string;
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
directive可以用exportAs
@Directive({
selector: '[appHighlightS]',
exportAs: 'appHightlightE' // 把Directive export出來
用reference template #f 去接 export 出來的 Directive appHightlightE
<p appHighlightS #f="appHightlightE">Highlight me!<p>
button 可以去觸發<p appHighlight>裡面的directive裡面的function()
<button (click)="f.showMessage()">show message</button>
<input name="firstName" ngModel #firstName="ngModel" />
<button [disabled]="firstName.invalid">
https://angular.io/guide/structural-directives
selector 是 [ngFor] 或 [ngForOf]
開啟文件
<ul>
<li *ngFor="let item of [1,2,3]; let i=index"> {{i}} - {{ item }} </li>
0,1,2 index就是exported出來的,可當區域變數
</ul>
<T>
<li *ngFor="let user of userObservable | async as users; index as i; first as isFirst">
{{i}}/{{users.length}}. {{user}} <span *ngIf="isFirst">default</span>
</li>
https://angular.io/api/common/NgForOf#ngForTrackBy
用途:當iterable在異動項目時,最小化DOM的變更,只重新改變「異動的項目」
說明:預設change detector假設object instance來identify(識別)iterable裡的node(項目)
若提供TrackByFunction,Directive用function的return的結果來識別 item node,
而不是識別 object本身
文件裡只有TrackByFunction<T>
,只好用範例裡的trackBy: trackById
https://stackblitz.com/angular/oaypkvmqklj
@Input() ngForTrackBy
: TrackByFunction<T>
// 後面接要track的function()
<li *ngFor="let item of items; index as i; trackBy: trackById">...</li>
簡寫
或用<ng-template>去包<li>
<ng-template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackById">
<li>...</li>
</ng-template>
export class Item {
id: number; // key
name: string;
}
export const items: Item[] = [
{ id:1, name:'alice'},
{ id:2, name:'bob'}
];
export class AppComponent {
items = items;
item = this.items[0];
// trace iterable中項目異動時的function
trackById(index: number, item: Item): number { return item.id; }
// 文件是寫傳入 iteration index, node object ID 不解??
}
基礎的Kevin老師用官網的範例帶過
<div [ngSwitch]="hero?.emotion">
<app-happy-hero *ngSwitchCase="'happy'" [hero]="hero"></app-happy-hero>
<app-sad-hero *ngSwitchCase="'sad'" [hero]="hero"></app-sad-hero>
<app-confused-hero *ngSwitchCase="'confused'" [hero]="hero"></app-confused-hero>
<app-unknown-hero *ngSwitchDefault [hero]="hero"></app-unknown-hero>
</div>
再來看到Mike大大的大作「[Angular 大師之路] Day 11 - *ngIf 有 else 可以用嗎?」
https://ithelp.ithome.com.tw/articles/10205494
<ng-template>
<div *ngIf="display;then tmpRefId4then else tmpRefId4else">Hello World</div>
<button (click)="display = !display">display switch</button>
<ng-template #tmpRefId4then> 設定 template reference id
when then case to show
</ng-template>
<ng-template #tmpRefId4else> 設定 template reference id
when else case to show
</ng-template>
*
做了哪些事?參考文件
https://angular.io/guide/structural-directives#prefer-the-asterisk--syntax
如何自定義 類似 let item of items 的 directive?
Write a structural directive
https://angular.io/guide/structural-directives#unless
程式在app.component.html的最下面,自行研究囉,我也看不懂,不敢亂解釋
https://stackblitz.com/angular/oaypkvmqklj
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[appUnless]'})
export class UnlessDirective {
private hasView = false;
constructor(
// 看來關鍵在於 注入的 TemplateRef、ViewContainerRef 的操作
// TemplateRef、ViewContainerRef 屬於 API,可詳讀
// https://angular.io/api/core/ElementRef
// https://angular.io/api/core/ViewContainerRef
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
}
<h4>UnlessDirective with template</h4>
condition是boolean;
<p *appUnless="condition">Show this sentence unless the condition is true.</p>
<p *appUnless="condition" class="code unless">
(A) <p *appUnless="condition" class="code unless">
</p>
<ng-template [appUnless]="condition">
<p class="code unless">
(A) <ng-template [appUnless]="condition">
</p>
</ng-template>
找renderder2,選API,C的(class)
用Renderer2會再多包一層,相容性應該會比較好,減少跨平台的錯誤訊息
https://angular.io/api/core/Renderer2
不要用Document.createElement()
https://developer.mozilla.org/zh-TW/docs/Web/API/Document/createElement
如果有用listen,記得取消掉,否則會一直存在
import {Directive,Renderer2, Input, ElementRef, OnChange, AfterViewInit' from '@angular/core';
const centerWays = {
0: {
parent: {},
own: {
'position: 'absolute',
'width': '400px',
'top': '50%',
'left': '50%',
'margin-left': '-200px',
'margin-top': '35
}
}
}
@Directive({
selector: '[amosCenter]'
})
export class CenterDirective implements AfterViewInit, OnChanges{
@Input('amosCenter') way = 0;
dom = this.ele.nativeElement as HTMLDivElement;
parentStyle;
constructor(
private ele: ElementRef, // 重點是API有哪些Properties、Methods可用
Private render: Renderer2
){}
// @Input值有變的時候觸發
ngOnChanges() {
this.parentStyle = centerWays[this.way || 0].parent;
}
// ViewInit後觸發
ngAfterViewInit(){
this.setParentStyle();
}
private setParentStyle() {
Object.entries(this.parentStyle).forEach(item => {
this.render.setStyle(this.dom.parentElement, item[0], item[1]);
});
}
}