LoginSignup
28
24

More than 5 years have passed since last update.

Angularで動的にコンポーネントを追加する

Last updated at Posted at 2019-01-08

ボタンをクリックした時に、Angularコンポーネントを動的に挿入する方法を記載します。

以下の2つに分けて解説しています。

  1. Angularコンポーネントを動的に挿入(→ まずは場所を考えずに挿入します。)
  2. 特定の場所に、Angularコンポーネントを動的に挿入

Step1. Angularコンポーネントを動的に挿入

動的に挿入するコンポーネントを作成

ここでは、以下のようなとても簡単なコンポーネントを作成します。

src/app/dynamic/dynamic.component.ts
...
import { Component } from '@angular/core';

@Component({
  selector: 'app-dynamic',
  template: `<p>dynamic works!</p>`
})
export class DynamicComponent {

}

ボタンをhtml上に配置

ボタンをクリックすると、メソッド onButtonClick() が実行され、Angularコンポーネントを生成できるようにします。

src/app/app.component.html
...
<div>
  <button (click)="onButtonClick()">click!</button>
</div>

動的にAngularコンポーネントを挿入

ComponentFactoryviewContainerRef を利用し、Angularコンポーネントを挿入します。

  • 初期処理 ngOnInit() で、動的に DynamicComponent をコンパイルできる状態にします。
  • クリックされた時に、 viewContainerRef.createComponent を利用し、コンポーネント生成を実行します。
src/app/app.component.ts
...
import { Component, ComponentFactory, ComponentFactoryResolver, OnInit, ViewContainerRef } from '@angular/core';
import { DynamicComponent } from './dynamic/dynamic.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  factory: ComponentFactory<DynamicComponent>;

  constructor(
    public viewContainerRef: ViewContainerRef,
    private resolver: ComponentFactoryResolver) { }

  ngOnInit() {
    this.factory = this.resolver.resolveComponentFactory(DynamicComponent);
  }

  onButtonClick() {
    this.viewContainerRef.createComponent(this.factory);
  }
}

@NgModule.entryComponents に、動的に挿入するコンポーネントを追加

(3)まで実装し、ボタンをクリックすると、ERROR Error: No component factory found for DynamicComponent. Did you add it to @NgModule.entryComponents? というエラーが表示されます。

指示される通りに entryComponents を追加すると、ボタンを押すと動的にAngularコンポーネントを表示するところまで完成します。

src/app/app.module.ts
...
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { DynamicComponent } from './dynamic/dynamic.component';


@NgModule({
  declarations: [
    AppComponent,
    DynamicComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [DynamicComponent] // Add entryComponents here!
})
export class AppModule { }

ソース全体の確認はこちら

Step2. 特定の場所に、Angularコンポーネントを動的に挿入

(Step1 が完了している前提で) @ViewChild を利用することで、コンポーネントを置く場所を指定することが可能になります。component.tsとhtmlをそれぞれ編集します。

src/app/app.component.ts
...
export class AppComponent implements OnInit {
  factory: ComponentFactory<DynamicComponent>;
  // @ViewChild を追加
  @ViewChild('dynamic', { read: ViewContainerRef }) viewContainerRef: ViewContainerRef;

  // 不要になった ViewContainerRef をコンストラクタから削除
  constructor(
    private resolver: ComponentFactoryResolver) { }

以下略

htmlには、コンポーネントを挿入したい箇所に、上記の @ViewChild 定義した #dynamic タグを追加します。以下では、ボタンの上にコンポーネントが生成されるようにします。

src/app/app.component.html
...
<div #dynamic></div>
<div>
  <button (click)="onButtonClick()">click!</button>
</div>

上記を完了したうえでボタンをクリックすると、ボタンの上に"dynamic works!"と表示されることが確認できます。

ソース全体の確認はこちら

補足

  • 今回の実装ではDOM要素を追加しているだけなので、DomSanitizerを利用して実装することもできます。
  • コンポーネントを生成するロジックをServiceクラスに置くことで、ほかのコンポーネントから呼び出すことができるようにできます。詳しくは「参考」に載せているページで確認できます。

参考

28
24
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
24