既然 Pipe 可以自己設計,那麼 Directive 當然也可以自己做,這邊要講的是如何自己設計一個 Attribute Directive。
Angular-CLI 的 command 我們已經很熟了,下指令建立一個 Directive:
ng generate directive <directive name>
alias:
ng g d <directive name>
跟之前一樣,Angular 會建立檔案,且修改 app.module.ts
我這邊做的功能是,按下按鈕時,按鈕上的字串會被打亂,鬆開按鈕時又換成顯示原本的字串。
完整程式碼:
// homepage.component.ts
text = '漢字的順序並不影響閱讀';
// homepage.component.html
<input type="submit" [appRandom]="text" [value]="text">
// random.directive.ts
@Directive({
selector: '[appRandom]'
})
export class RandomDirective {
@Input('appRandom') DOMtext: string;
el: ElementRef;
constructor(element: ElementRef) {
this.el = element;
}
@HostListener('mousedown') onMouseDown() {
const length = this.DOMtext.length;
const randArr = [];
while (randArr.length < length) {
const rand = Math.round(Math.random() * (this.DOMtext.length - 1));
if (!randArr.includes(rand)) { // 產生的隨機值沒有出現再陣列中,才放進去
randArr.push(rand);
}
}
let newStr = '';
for (const i of randArr) {
newStr += this.DOMtext[randArr[i]];
}
this.el.nativeElement.value = newStr;
}
@HostListener('mouseup') onMouseUp() {
this.el.nativeElement.value = this.DOMtext;
}
}
@Input('appRandom') DOMtext: string;
el: ElementRef;
constructor(element: ElementRef) {
this.el = element;
}
我在 RandomDirective
裡面宣告一個變數叫 DOMtext
,前面加上 @Input()
這個 Decorator
,所以外部可以用 Property Binding,將變數傳入,就是 [appRandom]="text"
,將 text
傳進 @Input()
的部分。
接著我用 ElementRef
,在constructor(element: ElementRef)
初始化的時候,傳入一個 element
,這裡會傳入在 homepage.component.html
的 <input>
,如此一來,我們就可以直接對 DOM 進行操作,
@HostListener('mousedown') onMouseDown() {
...
}
@HostListener('mouseup') onMouseUp() {
...
}
@HostListener()
用來監聽事件,像 @HostListener('mousedown') onMouseDown()
就是監聽 mousedown
這個按下滑鼠的事件,並綁定到 onMouseDown()
這個自訂的 function。@HostListener('mouseup') onMouseUp()
則是監聽滑鼠鬆開的事件。
@HostListener('mousedown') onMouseDown() {
const length = this.DOMtext.length;
const randArr = [];
while (randArr.length < length) {
const rand = Math.round(Math.random() * (this.DOMtext.length - 1));
if (!randArr.includes(rand)) {
randArr.push(rand);
}
}
let newStr = '';
for (const i of randArr) {
newStr += this.DOMtext[randArr[i]];
}
this.el.nativeElement.value = newStr;
}
中間一大段程式,是我根據 DOMtext
,產生一個一樣長度的陣列,裡面放的是打亂的 index
,再根據這個 index,產生一個新的、隨機排列過的字串。
先用 Math.random()
產生 0 ~ 1的浮點數,再乘上length - 1
。假設我的字串長度是 12,那我需要產生範圍 0 ~ 11 的亂數,所以會是 length - 1
,最後再用 Math.round()
做四捨五入,最後我就能生出一些這樣的陣列:
再用這個陣列的順序,將舊的字串,依照順序,每個字新增到 newStr
後面,就可以得到一個隨機排列過的字串。
最後用 this.el.nativeElement.value = newStr;
改變 DOM顯示的 value,所顯示的就會是新的字串。
當我放開滑鼠時,再將原先的 this.DOMtext
,送到 DOM 的 value,讓它顯示隨機排列前的文字,這也是為什麼我們不對 this.DOMtext
進行修改的原因。
執行結果: