@ViewChild 裝飾器

基本概念

@ViewChild 是一個裝飾器,可以在 DOM 中獲取包含特定模板變量或元件的元素。它用於選擇在元件內的 template,並使這些元素在元件的 class 中可用。

基本語法

@ViewChild(selector, { static?: boolean }) name: ElementRef<Type>;
  • selector:可以是 component、directive、template reference variable(字串)或 TemplateRef,但不能是 CSS class。

  • static:可選參數,決定查詢結果什麼時候解析。

    • static: true - 在 ngOnInit 中可用。

    • static: false(默認值)- 在 ngAfterViewInit 中可用。

  • name:元件類中用來存儲查詢結果的屬性名。

  • Type:選擇器獲取的元素類型。

使用範例

使用 component 作為 selector

當 selector 是元件類型時,@ViewChild 會在模板中找到該元件的第一個實例,並把這個實例儲存在命名變量中。

@ViewChild(ButtonComponent) button?: ButtonComponent;

這樣就可以在 component class 中通過 button 屬性與 ButtonComponent 的實例進行交互。

使用模板變量名作為選擇器

另一個常見用法是把 selector 設定成字串,這個字串是模板變量名(不帶 # 符號)。

例如,假設模板中有:

<form (ngSubmit)="onSubmit(titleInput.value, textInput.value)" #form>
  <!-- 表單內容 -->
</form>

在元件類中可以這樣獲取:

@ViewChild('form') formContent?: ElementRef<HTMLFormElement>;

這告訴 Angular 應該在模板中找帶有 #form 的元素,並把該元素存進 formContent 中。

ElementRef 與泛型

ElementRef 是一個泛型類,它是 Angular 對原生 DOM 元素的包裝。通過泛型參數,我們可以指定被包裝元素的類型,使 TypeScript 能夠提供更好的類型檢查和智能提示。

所以需要額外的訊息來知道被包裹住的值的型別。所以在後面接的是 selector 選擇的值的類型,放在 <> 中,在這個例子中是 HTMLFormElement

要訪問實際的 DOM 元素,需要通過 .nativeElement 屬性:

this.formContent?.nativeElement.reset();

放在 onSubmit() 裡面,在表單被送出的時候會清空內容。

生命週期注意事項

  • @ViewChild 引用的元素在元件初始化時(構造函數中)還無法訪問

  • 根據 static 參數的不同,可以在 ngOnInit(當 static: true)或 ngAfterViewInit(當 static: false)生命週期鉤子中訪問

ngAfterViewInit() {
  // 這裡可以安全地訪問 ViewChild 元素
  if (this.formContent) {
    console.log(this.formContent.nativeElement);
  }
}

實際應用範例

清空表單:

@ViewChild('form') formContent?: ElementRef<HTMLFormElement>;

onSubmit(title: string, text: string) {
  // 處理表單提交邏輯
  
  // 重置表單
  this.formContent?.nativeElement.reset();
}

注意事項:

  • 不能向 selector 傳遞 CSS 選擇器,如 .control

  • 使用模板變量名作為選擇器時不需要帶 # 符號

  • 如果元素可能不存在,應使用可選屬性(?)來避免錯誤

  • 還有 @ViewChildren 這個變體

  • 可以加上 private:

 @ViewChild('form') private formContent?: ElementRef<HTMLFormElement>;
  • 在 Angular 17+,可以改成用 viewChild(),會給你 Signal value。

private formContent = viewChild<ElementRef<HTMLFormElement>>('form')
this.formContent()?.nativeElement.reset()

不想用問號的話可以用 .required 方法

private formContent = viewChild.required<ElementRef<HTMLFormElement>>('form')

Last updated