Skip to main content

Directive Dynamic Operations

目的

Directive 可以寄生在宿主元件(Host Element)上,藉此來改變該元件的行為
但有時需要動態新增元素或是取得宿主元素進行操作,因此整理以下使用情境

Common APIs​

在介紹情境之前,來先了解一些 Angular 常用的 API:

  • ViewContainerRef: 負責管理動態元件的特殊容器,它提供了創建、插入、刪除元件等操作
  • ElementRef: 用於直接代表元件的物件,可透過它提供的nativeElement存取元件的 DOM,可以想像它就是將包元件 DOM 在包一層的物件
  • Renderer2: Angular 所提供的一種可以操作元素的物件,它提供了一組方法來操作 DOM 元素,讓我們不需要直接操作底層的 DOM API,並且可用於跨平台的 DOM 操作

Use Cases​

這邊分別列出不同情境的目的,以及要如何在 Directive 內進行實作, 範例程式碼可以參考 Live Demo (Angular 16)

Sibling Element​

在宿主元件後動態新增元素,也就是兄弟節點(Sibling)

這是最常見且最基本的情境,透過注入ViewContainerRef並使用createComponent來動態創建元件:

constructor(private viewContainerRef: ViewContainerRef) {};

ngOnInit() {
const component = this.viewContainerRef.createComponent(DynamicComponent);
}
ComponentRef<T>

ViewContainerRef.createComponent() 會回傳新元素的ComponentRef物件,它提供location屬性來存取原生 DOM,以及instance可取得 Angular 的元件實體 😆

createComponent預設會創建一個元件在宿主元素之後,產生結果示意如下:

<host-element></host-element>
<new-element></new-element> <== sibling here

Parent Element​

在宿主元素外新增一層元素,也就是父節點(Parent)

這邊除了ViewContainerRef創建父元素以外,還需要透過Renderer2提供的appendChild,將宿主元素添加進父元素內:

  constructor(
private elementRef: ElementRef,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef
) {}

ngOnInit() {
const component = this.viewContainerRef.createComponent(DynamicComponent);
this.renderer.appendChild(
component.location.nativeElement,
this.elementRef.nativeElement
);
}

產生結果如下示意:

<new-element> <== parent here
<host-element></host-element>
</new-element>

Child Element​

在宿主元素內新增元素,也就是子節點(Child)

同樣透過Renderer2提供的appendChild,將新增元素添加進父元素內:

  constructor(
private elementRef: ElementRef,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef
) {}

ngOnInit() {
const component = this.viewContainerRef.createComponent(DynamicComponent);
this.renderer.appendChild(
this.elementRef.nativeElement,
component.location.nativeElement
);
}

產生結果如下示意:

  <host-element>
<new-element> <== child here
</new-element>
</host-element>

Access Child Element​

存取宿主元素內的子元素,並進行修改

透過ViewContainerRef可以拿到宿主元素的 DOM,再使用DOM API對元素內進行搜尋,這邊使用querySelector

  ngAfterViewInit() {
const p = this.viewContainerRef.element.nativeElement.querySelector('p');
p.style.color = 'red';
p.textContent = 'I have been changed!';
}

constructor(private viewContainerRef: ViewContainerRef) {}