--- tags: [angular] title: Decorators date: 2022-05-27 00:23:21 categories: FrontEnd --- > Decorators 是 function 掛有 `@`前綴符號,可以用於 `class`、`paramemter`、`method` 或 `property`的前面。用來提供額外的資訊。 ## HostListener @hostListener 可以監聽宿主元素上的事件,監聽 JS 的 event click | keydown | mouseleave ... ```tsx class CountClicks { numberOfClicks = 0; // 這裡的 onClick 可以自行命名 @HostListener("window:click", ["$event"]) onClick(btn): void { console.log("button", btn, "number of clicks:", this.numberOfClicks++); } } ``` ## HostBinding @HostBinding()可以為指令的宿主元素新增類、樣式、屬性等 @HostBinding()和@HostListener()不僅僅用在自定義指令,只是在自定義指令中用的較多 下面我們通過實現一個在輸入時實時改變字型和邊框顏色的指令,學習@HostBinding()和@HostListener()的用法。 ```js export class AddRainbowDirective { constructor() {} possibleColors = ["darksalmon", "hotpink", "lightskyblue", "goldenrod", "peachpuff", "mediumspringgreen", "cornflowerblue", "blanchedalmond", "lightslategrey"]; @HostBinding("style.color") color: string; @HostBinding("style.borderColor") borderColor: string; @HostListener("keydown") onKeydown() { const colorPick = Math.floor(Math.random() * this.possibleColors.length); this.color = this.borderColor = this.possibleColors[colorPick]; } } ``` ```html <input appRainbow /> ``` ## NgModule - **declarations**-屬於此 NgModule 的 Component、Directive 與 Pipe 皆放置於此。 - **imports**-此 NgModule 需要使用、依賴的其他 NgModule 皆放置於此(好像有點饒舌)。 - **providers**-可以被整個應用程式中的任何部分被使用的 Service 皆放置於此,也可以將 Service 直接放置在 Component 的 Metadata 裡的  `providers` *(但放置在不同地方會有一些需要特別注意的事項,後續在說明 Service 時會提到。另外,在 Angular 6 之後,在 Service 之中也可以使用 Metadata 裡的  `providedIn`  宣告,該 Service 要  `provided`  到哪裡。詳細可以參考[此篇文章](https://blog.ninja-squad.com/2018/05/04/what-is-new-angular-6/)或是隔壁棚[Angular 大師之路](https://ithelp.ithome.com.tw/articles/10203876)也有提到)* 。 - **exports**-此處放置的是,當在其他 NgModule 裡 import 了當前的 Module 後,可以在其他 NgModule 裡的 Component Template 使用的 Component、Directive 與 Pipe。 - **entryComponents**-放在這裡的元件通常是用不通過 Route 的方式,而採用動態加入的元件。 - **bootstrap**-在此設置的是應用程式通常稱之為 Root Component *(根元件)* ,而且只有 Root Module 才要設置此屬性。 [[功能介紹-11] NgModules - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天](https://ithelp.ithome.com.tw/articles/10195338) ## Input 父層的 html ```tsx <app-bank-account [bank]="RBC" [account-id]="4747"></app-bank-account> ``` 子層的 component ```tsx class BankAccount { @Input() bankName: string; @Input("account-id") id: string; //可以用此方把名稱改為自已想要的東西 // TODO: 還有一個可以直接 set() 值 } ``` ## Output 要使用 output 有三個步驟 1. 引入 Output & EventEmitter 的 function 在 angular 2. 讓你的 Output 有一個 EventEmitter 3. 建立一個 function 把資料 emt 上去 ```tsx import { Output, EventEmitter } from '@angular/core'; // step1 @Output() newItemEvent = new EventEmitter<string>(); // step2 addNewItem(value: string) { this.newItemEvent.emit(value); // step3 用這個方式 emit 資料上去 } } // html <app-item (newItemEvent)="addItem($event)"></app-item> ``` ## VeiwChild 抓取 DOM 也可以抓取 component 得到裡面的定義變數的值 ```jsx @ViewChild('historyBlock') historyBlock; ngOnInit(): void { console.log(this.historyBlock.nativeElement) // get DOM } // html <div #historyBlock> </div> ``` ## VeiwChildren 用於配置檢視查詢的引數裝飾器。 ```html <ng-container *ngFor="let item of arrName"> <hello #item [name]="item"></hello> </ng-container> ``` ```jsx import { AfterViewInit, Component, VERSION, ViewChild } from "@angular/core"; import { HelloComponent } from "./hello.component"; @Component({ selector: "my-app", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"], }) export class AppComponent implements AfterViewInit { arrName = ["aa", "bb", "cc"]; @ViewChildren("item") itemElement: QueryList<HelloComponent>; ngAfterViewInit() { this.itemElement.map((e) => { console.log("itemElement", e.name); // 依序提出子組件裡的值 }); } } ``` 注意:`ViewChildren`  一定要搭配  `QueryList`  一起使用,不然就不能實現列表更新這件事了因生命週期關係  `ViewChild`  與  `ViewChildren`  要在  `ngAfterViewInit`  裡才能實現 ## Injectable & Inject Angular 其實提供了三種註冊 Service 的方式。 ### inject 第一種 第一種方式最簡單,也是 Angular CLI 在產生 Service 時預設使用的方式: 這種方式是在 Service 自己的 Metadata 裡加上 `providedIn: 'root'` 的屬性,來告訴 Angular 說:「請把我註冊在整個系統都是使用同一個實體的注射器裡」。 ```js @Injectable({ providedIn: 'root' }) ``` ### inject 第二種 這種方式是告訴 Angular 說:「請把我註冊在這整個 NgModule 都用同一個實體的注射器裡」。也就是說,假設當初 A Service 是註冊在 A Module 裡,那麼在整個 A Module 裡就會使用同一個實體。 ```js @NgModule({ providers: [ BackendService, Logger ], ... }) ``` > 那在 B Module 裡可以用 A Service 嗎? 可以。 > > 那在 B Module 裡的 A Service 跟在 A Module 裡的 A Service 是同一個實體嗎?同一個<心中存疑??> ### inject 第三種 這種方式是告訴 Angular 說:「請把我註冊在這整個 Component 都用同一個實體的注射器裡」。意思是每個 Component 拿到的 Service 實體都不是同一個。 絕對不是同一個 ```js @Component({ selector: 'app-hero-list', templateUrl: './hero-list.component.html', providers: [ HeroService ] }) ``` 這個要常常看一下,重要 https://blog.kevinyang.net/2018/01/19/angular-viewproviders-providers/ ## 參考文章 [自訂 Decorators](https://blog.kevinyang.net/2017/01/30/angular2-decorators/) [HostBinding&HostListener](https://jiaming0708.github.io/2017/03/27/angular-hostbinding-listener/) [HostBinding&HostListener](https://jiaming0708.github.io/2017/03/27/angular-hostbinding-listener/)