當我們在Angular中想要操作Template的DOM時,我們通常會在該DOM上加入範本參考變數(Template reference variables)
再經由ViewChild去取得,但並不是所有情況都可以取得到畫面上的DOM,因此本文主要介紹為什麼會遇到undefined問題
以及如何解決。
前陣子剛好接觸一個Angular專案,該專案需要在彈跳視窗中能在input輸入框中輸入內容,並根據關鍵字進行篩選。
這功能其實非常常見,筆者自己慣用的方法主要是透過Observable
來訂閱該輸入框,並能即時取得使用者所輸入的內容。
所以一般程式碼大概會長得像這個樣子:
Template:
<input type="text" [(ngModel)]="inputSearch" #inputKey="ngModel">
Component:
export class InputSearchComponent implements AfterViewInit {
@ViewChild('inputKey') key: NgModel;
constructor() { }
inputSearch;
ngAfterViewInit(): void {
this.key.valueChanges.pipe(debounceTime(1000)).subscribe(x => console.log(x));
}
但正當我在該專案寫上這樣的Code時,卻回報該錯誤訊息:
看到錯誤訊息的當下還一度以為自己是不是哪裡打錯了,以往都不會發生的錯誤竟然在這裡發生。
後來進一步追查程式碼後,發現其實是因為 ngIf
在搞鬼。
因為我的專案需求是當彈跳視窗出現時,使用者可以根據輸入框填寫關鍵字,進而查詢所需要的資訊。
在這裡我使用了ngIf
,當按鈕按下後,該彈跳視窗就可以顯示在畫面上,相反的如果條件不成立,則不顯示。
而其中ViewChild
在Component當中,只會執行一次,也就是當畫面載入完成時,會透過範本參考變數來抓取我們的DOM。
問題就出在這個地方,因為ngIf
並不是直接將該區塊隱藏起來(display:none)
,而是直接將該區塊加入/銷毀。
因此在一開始ngIf
條件為false時,該區塊並不會顯示在整個DOM樹上。
所以ViewChild
取不到物件,自然就會回傳undefined
。而當undefined
後,又去呼叫valueChanges
當然就沒有這個方法啦!,所以才會回報錯誤訊息。
那,現在我們知道問題發生的點了,我們可以怎麼做?
筆者這裡提供兩個解法供讀者們使用
1. 改用hidden
在Angular中,有提供屬性[hidden]
在這邊我們一樣可以透過條件式決定是否顯示,此外由於是隱藏(display:none)
的關係,因此在DOM上還是存在的,
所以ViewChild就可以透過範本參考變數取得我們所需的DOM元件。
2.透過ChangeDetectorRef 再次偵測畫面上的DOM樹
由於剛剛我們有提到ViewChild
只會在第一次的時候抓取DOM樹,因此即使後面ngIf
條件為True可以顯示內容時,仍然不會去抓取,因此我們可以透過ChangeDetectorRef
類別
當我們注入ChangeDetectorRef
類別後,我們可以透過 detectChanges()
方法,偵測DOM樹有無變更,若變更的話,ViewChild
就會在進行一次抓取
透過這樣的方式我們就可以取得畫面上的元件了,無論該DOM是否有在畫面上。
以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教:)
有任何家教、案子 或技術相關問題 請都歡迎聯繫我