コンテンツへスキップ
ViewChildとContentChildはAngularで何でしょうか?

ViewChildとContentChildはAngularで何でしょうか?

このブログではAngular ViewChild と ContentChild を使用して、コンポーネントのビュー内の子コンポーネントと DOM 要素にアクセスして操作する方法を示します。すべて読む。

7min read

ViewChildおよびContentChildはコンポーネント通信にAngularに使用されます。例えば、親Angularコンポーネントが子コンポーネントにアクセスしたい場合は、ViewChildまたはContentChildを使用します。

このブログ記事では、Angular ViewChildとAngular ContentChildの本質を説明し、アプリ内での使い方をご紹介します。

ViewChildとは何かAngular

Angular ViewChildまたはViewChildrenデコレーターは、コンポーネント参照名またはクラス名を使ってテンプレートにクエリを送り出し、コンポーネントインスタンスへの参照を取得するために使用されます。Angularの ViewChild最初の一致するコンポーネントを返し、ViewChildrenはすべての一致するコンポーネントをQueryListとして返します。これらの参照を使ってコンポーネントクラスやそのDOM要素を扱うことができます。 

親コンポーネント内で以下の機能にアクセスしたい場合は、Angularのデコレーター@ViewChild使ってください。

Using MessageComponent

以下のリストに示されているようにMessageComponentがあると仮定しましょう:

import { Component, Input } from "@angular/core";
@Component({
    selector: "app-message",
    template: `<h2>{{message}}</h2>`,
})
export class MessageComponent {
    @Input() message: string;
}

AppComponent内のMessageComponentを使っています。こちらに示されている通りです:

import { Component, OnInit } from "@angular/core";
@Component({
    selector: "app-root",
    template: ` <div>
        <h1>Messages</h1>
        <app-message [message]="message"></app-message>
    </div>`,
})
export class AppComponent implements OnInit {
    message: any;
    ngOnInit() {
        this.message = "Hello World !";
    }
}

MessageComponentはAppComponentのテンプレート内に配置されます。したがって、ViewChildとしてアクセスできます。

export class AppComponent implements OnInit, AfterViewInit {
    message: any;
    @ViewChild(MessageComponent) messageComponent: MessageComponent;

    ngAfterViewInit() {
        console.log(this.messageComponent);
    }

    ngOnInit() {
        this.message = "Hello World !";
    }
}

MessageComponentの値の変更

では、MessageComponentプロパティの値を変更してみます。

ngAfterViewInit() {
   console.log(this.messageComponent);
   this.messageComponent.message = 'Passed as View Child';
}

ここではViewChildプロパティの値を変更しています。

ViewChildrenとQueryList Angular使う方法

私たちは *ngFor 指令内で MessageComponent を使用しています;したがって、MessageComponentへの複数の参照があります。現在、以下のリストに示すようにViewChildrenおよびQueryListとしてアクセスできます。

@ViewChildren(MessageComponent) messageComponent: QueryList<MessageComponent>;
   ngAfterViewInit() {
    console.log(this.messageComponent);
   }

出力にはViewChildernとしてMessageComponentのさまざまな参照が表示されます。

では、以下のリストに示されているようにViewChildrenのプロパティを更新してみましょう。

ngAfterViewInit() {
   console.log(this.messageComponent);
   this.messageComponent.forEach((item) => { item.message = 'Infragistics'; });
}

ご覧の通り、私たちはAngular ViewChildrenの各項目を繰り返し、各プロパティを更新しています。これによりプロパティの値は更新されますが、やはり「最後のチェック後に式が変更されました」というエラーが出ます。

手動で変更検出(例えばViewChild)を呼び出すことで修正できます。AfterContentInitのライフサイクルフックにはViewChildren参照は利用できないことをご注意ください。以下の一覧に示されているように、ngAfterContentInit() ライフサイクルフックでUndefined ViewChildren参照が得られます:

ngAfterContentInit() {
   console.log(this.messageComponent); // undefined 
}

ただし、手動で「変更検出」を呼び出して「最後にチェックされた後に表現が変更されました」というエラーを修正できます

 変化検出機構を使用する

  1. Import ChangeDetectorRef from @angular/core
  2. コンポーネントクラスのコンストラクタに注入します
  3. ViewChildプロパティが変更された後にdetectChanges()メソッドを呼び出します

以下のリストにあるような手動の変更検出を使うことができます:

@ViewChildren(MessageComponent) messageComponent: QueryList<MessageComponent>;
	constructor(private cdr: ChangeDetectorRef) {
}
ngAfterViewInit() {
	console.log(this.messageComponent);
	this.messageComponent.forEach((item) => { item.message = 'Infragistics'; });
	this.cdr.detectChanges();
}

ContentChildとは何Angular?

Angular ContentChildプロパティデコレーターはViewChildに非常に似ており、コンポーネント内の投影コンテンツと操作する強力な手段を提供します。これを使えば、DOM内のProjected Contentへの参照を簡単に取得できます。

まずはContentChildについて理解しましょう。テンプレート内に存在する任意の要素はContentChildです。

理解するために、MessageContainerComponentを考えてみましょう。

import { Component } from "@angular/core";
@Component({
    selector: "app-messagecontainer",
    template: ` <div>
        <h3>{{greetMessage}}</h3>
        <ng-content select="app-message"></ng-content>
    </div>`,
})
export class MessageContainerComponent {
    greetMessage = "Ignite UI Rocks!";
}

このコンポーネントでは、コンテンツプロジェクションAngularを使用しています。

ContentChild Angular MessageComponentのパッシング

コンテンツ<に投影された任意の要素やコンポーネントは>ContentChildとなります。MessageContainerComponent内に投影されたMessageComponentにアクセスし、通信したい場合は、それをContentChildとして読み取る必要があります。

AngularでContentChildの使い方を学ぶ前に、まずMessageContainerComponentがどのように使われ、MessageComponentがどのように投影されているかを確認しましょう。

import { Component, OnInit } from "@angular/core";
@Component({
    selector: "app-root",
    template: ` <div>
        <app-messagecontainer>
            <app-message [message]="message"></app-message>
        </app-messagecontainer>
    </div>`,
})
export class AppComponent implements OnInit {
    message: any;
    ngOnInit() {
        this.message = "Hello World !";
    }
}

上記のリストにあるように、AppComponentでMessageContainerComponentを使用し、MessageComponentをその中に投影する形で渡しています。MessageComponentはコンテンツプロジェクションを伴うMessageContainerComponentで使用されるため、ContentChild Angularなります。

MessageComponnetはMessageContainerComponentのテンプレート内で投影され使用されるため、以下のようにContentChildとして使用できます。

import { Component, ContentChild, AfterContentInit } from "@angular/core";
import { MessageComponent } from "./message.component";

@Component({
    selector: "app-messagecontainer",
    template: ` <div>
        <h3>{{greetMessage}}</h3>
        <ng-content select="app-message"></ng-content>
    </div>`,
})
export class MessageContainerComponent implements AfterContentInit {
    greetMessage = "Ignite UI Rocks!";
    @ContentChild(MessageComponent) MessageComponentContentChild: MessageComponent;
    ngAfterContentInit() {
        console.log(this.MessageComponentContentChild);
    }
}

以下の作業を行う必要があります。

  1. @angular/coreからContentChildとAfterContentInitをインポートしてください。
  2. コンポーネントクラスにAfterContentInitのライフサイクルフックを実装してください。
  3. デコレーター@ContentChildで物件を作りましょう。
  4. ngAfterContentInitのライフサイクルフック内でアクセスしてください。

コンポーネントのngAfterContentInitライフサイクルフック内でContentChildプロパティを変更できます。以下のリストに示すように、複数のMessageComponentが投影されていると仮定します。

import { Component, OnInit } from "@angular/core";
@Component({
    selector: "app-root",
    template: ` <div>
        <app-messagecontainer>
            <app-message *ngFor="let m of messages" [message]="m"></app-message>
        </app-messagecontainer>
    </div>`,
})
export class AppComponent implements OnInit {
    messages: any;
    ngOnInit() {
        this.messages = this.getMessage();
    }
    getMessage() {
        return ["Hello India", "Which team is winning Super Bowl? ", "Have you checked Ignite UI ?", "Take your broken heart and make it to the art"];
    }
}

現在、複数のContentChildが存在するため、以下のリストに示されているようにContentChildrenとしてアクセスする必要があります。

export class MessageContainerComponent implements AfterContentInit {
    greetMessage = "Ignite UI Rocks!";
    @ContentChildren(MessageComponent) MessageComponentContentChild: QueryList<MessageComponent>;
    ngAfterContentInit() {
        console.log(this.MessageComponentContentChild);
    }
}

ContentChildrenとQueryListの活用

ContentChildrenとQueryListを扱うには、以下の作業を行う必要があります。

  1. @angular/coreからContentChildren、QueryList、AfterContentInitをインポートします。
  2. デコレーター@ContentChildrenでQueryList型のプロパティを作成します。
  3. ngAfterContentInit() ライフサイクルフックでContentChildren参照にアクセスしてください。

ContentChildrenで各アイテムをクエリし、プロパティを次のように変更できます:

ngAfterContentInit() {
   this.MessageComponentContentChild.forEach((m) => m.message = 'Foo');
}

これがContentChildrenをAngularで使う方法です。

まとめ

ViewChildとContentChildはAngularの非常に重要な機能です。これらは親コンポーネント内の子コンポーネントへのアクセスを可能にするために使われます。コンポーネントテンプレートに含まれる任意の指令、コンポーネント、要素はViewChildとしてアクセスされます。一方、<ng-content>内に投影された任意の要素やコンポーネントはContentChildとしてアクセスされます。

さらに ViewChild と ContentChild をAngularでシンプルにするために、機能豊富なIgnite UI for Angularコンポーネントライブラリを試してみてください。最速のAngularデータグリッドと60+の高性能チャートを提供し、現代の高性能Angularアプリを簡単に構築できます。

(Last Updated: 08.09.2023)

Ignite UI for Angular

デモを予約