詳細ガイド
コンポーネント

クエリによるコンポーネントの子要素への参照

Tip: このガイドでは、基本概念のガイドを読んでいることを前提としています。Angularを初めて使う場合は、まずこちらをお読みください。

コンポーネントは、子要素を見つけてそのインジェクターから値を読み取るクエリを定義できます。

開発者は、クエリを使用して、子コンポーネント、ディレクティブ、DOM要素などの参照を取得することがほとんどです。

クエリには、ビュークエリコンテンツクエリの2種類があります。

ビュークエリ

ビュークエリは、コンポーネントのビュー(コンポーネント自身のテンプレートで定義されている要素)にある要素から結果を取得します。@ViewChild デコレーターを使用して、単一の結果をクエリできます。

      
@Component({  selector: 'custom-card-header',  ...})export class CustomCardHeader {  text: string;}@Component({  selector: 'custom-card',  template: '<custom-card-header>Visit sunny California!</custom-card-header>',})export class CustomCard {  @ViewChild(CustomCardHeader) header: CustomCardHeader;  ngAfterViewInit() {    console.log(this.header.text);  }}

この例では、CustomCardコンポーネントは子要素のCustomCardHeaderをクエリし、ngAfterViewInitで結果にアクセスしています。

クエリが結果を見つけられない場合、その値はundefinedになります。これは、ターゲット要素がNgIfによって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化すると、@ViewChildの結果を最新の状態に保ちます。

ビュークエリの結果は、ngAfterViewInitライフサイクルメソッドで利用可能になります。この時点以前は、値はundefinedです。コンポーネントライフサイクルの詳細については、ライフサイクルセクションをご覧ください。

@ViewChildren デコレーターを使用して、複数の結果をクエリできます。

      
@Component({  selector: 'custom-card-action',  ...,})export class CustomCardAction {  text: string;}@Component({  selector: 'custom-card',  template: `    <custom-card-action>Save</custom-card-action>    <custom-card-action>Cancel</custom-card-action>  `,})export class CustomCard {  @ViewChildren(CustomCardAction) actions: QueryList<CustomCardAction>;  ngAfterViewInit() {    this.actions.forEach(action => {      console.log(action.text);    });  }}

@ViewChildrenは、クエリの結果を含むQueryListオブジェクトを作成します。changesプロパティを使用して、クエリの結果が時間とともに変化した場合に購読できます。

クエリはコンポーネントの境界を越えることは決してありません。 ビュークエリは、コンポーネントのテンプレートからのみ結果を取得できます。

コンテンツクエリ

コンテンツクエリは、コンポーネントの_コンテンツ_(コンポーネントが使用されているテンプレート内でコンポーネントにネストされた要素)にある要素から結果を取得します。@ContentChild デコレーターを使用して、単一の結果をクエリできます。

      
@Component({  selector: 'custom-toggle',  ...})export class CustomToggle {  text: string;}@Component({  selector: 'custom-expando',  ...})export class CustomExpando {  @ContentChild(CustomToggle) toggle: CustomToggle;  ngAfterContentInit() {    console.log(this.toggle.text);  }}@Component({  selector: 'user-profile',  template: `    <custom-expando>      <custom-toggle>Show</custom-toggle>    </custom-expando>  `})export class UserProfile { }

この例では、CustomExpandoコンポーネントは子要素のCustomToggleをクエリし、ngAfterContentInitで結果にアクセスしています。

クエリが結果を見つけられない場合、その値はundefinedになります。これは、ターゲット要素が存在しないか、NgIfによって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化すると、@ContentChildの結果を最新の状態に保ちます。

デフォルトでは、コンテンツクエリはコンポーネントの直接の子要素のみを見つけ、子孫にトラバースすることはありません。

コンテンツクエリの結果は、ngAfterContentInitライフサイクルメソッドで利用可能になります。この時点以前は、値はundefinedです。コンポーネントライフサイクルの詳細については、ライフサイクルセクションをご覧ください。

@ContentChildren デコレーターを使用して、複数の結果をクエリできます。

      
@Component({  selector: 'custom-menu-item',  ...})export class CustomMenuItem {  text: string;}@Component({  selector: 'custom-menu',  ...,})export class CustomMenu {  @ContentChildren(CustomMenuItem) items: QueryList<CustomMenuItem>;  ngAfterContentInit() {    this.items.forEach(item => {      console.log(item.text);    });  }}@Component({  selector: 'user-profile',  template: `    <custom-menu>      <custom-menu-item>Cheese</custom-menu-item>      <custom-menu-item>Tomato</custom-menu-item>    </custom-menu>  `})export class UserProfile { }

@ContentChildrenは、クエリの結果を含むQueryListオブジェクトを作成します。changesプロパティを使用して、クエリの結果が時間とともに変化した場合に購読できます。

クエリはコンポーネントの境界を越えることは決してありません。 コンテンツクエリは、コンポーネント自身と同じテンプレートからのみ結果を取得できます。

クエリロケーター

各クエリのデコレーターの最初の引数は、ロケーターです。

ほとんどの場合、コンポーネントまたはディレクティブをロケーターとして使用します。

代わりに、テンプレート参照変数 に対応する文字列ロケーターを指定できます。

      
@Component({  ...,  template: `    <button #save>Save</button>    <button #cancel>Cancel</button>  `})export class ActionBar {  @ViewChild('save') saveButton: ElementRef<HTMLButtonElement>;}

同じテンプレート参照変数を定義している要素が複数ある場合、クエリは最初に一致する要素を取得します。

Angularは、CSSセレクターをクエリのロケーターとしてサポートしていません。

クエリとインジェクターツリー

Tip: プロバイダーとAngularのインジェクションツリーの詳細については、依存性注入を参照してください。

より高度なケースでは、ProviderTokenをロケーターとして使用できます。これにより、コンポーネントとディレクティブのプロバイダーに基づいて要素を特定できます。

      
const SUB_ITEM = new InjectionToken<string>('sub-item');@Component({  ...,  providers: [{provide: SUB_ITEM, useValue: 'special-item'}],})export class SpecialItem { }@Component({...})export class CustomList {  @ContentChild(SUB_ITEM) subItemType: string;}

上記の例では、InjectionTokenをロケーターとして使用していますが、特定の要素を特定するために、任意のProviderTokenを使用できます。

クエリオプション

すべてのクエリデコレーターは、2番目のパラメーターとしてオプションオブジェクトを受け取ります。これらのオプションは、クエリが結果をどのように見つけるかを制御します。

静的クエリ

@ViewChildおよび@ContentChildクエリは、staticオプションを受け取ります。

      
@Component({  selector: 'custom-card',  template: '<custom-card-header>Visit sunny California!</custom-card-header>',})export class CustomCard {  @ViewChild(CustomCardHeader, {static: true}) header: CustomCardHeader;  ngOnInit() {    console.log(this.header.text);  }}

static: trueを設定することで、Angularにこのクエリのターゲットが常に存在し、条件付きでレンダリングされていないことを保証します。これにより、結果はより早い段階で、ngOnInitライフサイクルメソッドで利用可能になります。

静的クエリの結果は、初期化後に更新されません。

staticオプションは、@ViewChildrenおよび@ContentChildrenクエリでは使用できません。

コンテンツ子孫

デフォルトでは、コンテンツクエリはコンポーネントの_直接_の子要素のみを見つけ、子孫にトラバースすることはありません。

      
@Component({  selector: 'custom-expando',  ...})export class CustomExpando {  @ContentChild(CustomToggle) toggle: CustomToggle;}@Component({  selector: 'user-profile',  template: `    <custom-expando>      <some-other-component>        <!-- custom-toggle will not be found! -->        <custom-toggle>Show</custom-toggle>      </some-other-component>    </custom-expando>  `})export class UserProfile { }

上記の例では、CustomExpandoは、<custom-toggle><custom-expando>の直接の子要素ではないため、<custom-toggle>を見つけることができません。descendants: trueを設定することで、クエリが同じテンプレート内のすべての子孫をトラバースするように構成できます。ただし、クエリは、_決して_コンポーネントに侵入して他のテンプレート内の要素をトラバースすることはありません。

ビュークエリには、子孫を_常に_トラバースするため、このオプションはありません。

要素のインジェクターからの特定の値の読み取り

デフォルトでは、クエリロケーターは、検索する要素と取得する値の両方を示します。代わりに、readオプションを指定して、ロケーターによって一致する要素から別の値を取得できます。

      
@Component({...})export class CustomExpando {  @ContentChild(ExpandoContent, {read: TemplateRef}) toggle: TemplateRef;}

上記の例では、ExpandoContentディレクティブを持つ要素を特定し、 その要素に関連付けられたTemplateRefを取得します。

開発者は、readを使用して、ElementRefTemplateRefを取得することがほとんどです。

QueryList の使用

@ViewChildren@ContentChildrenはどちらも、結果のリストを含むQueryListオブジェクトを提供します。

QueryListは、mapreduceforEachなどの配列のような方法で結果を操作するための便利なAPIをいくつか提供します。toArrayを呼び出すことで、現在の結果の配列を取得できます。

changesプロパティを購読して、結果が変更されるたびに何かを行うことができます。

クエリの一般的な落とし穴

クエリを使用する際に、コードの理解と保守を難しくする一般的な落とし穴があります。

複数のコンポーネント間で共有される状態には、常に単一の真実の源を維持します。これにより、異なるコンポーネントで状態が繰り返し使用され、同期が乱れるシナリオを防ぐことができます。

子コンポーネントに直接状態を書き込まないでください。このパターンは、理解が難しく、ExpressionChangedAfterItHasBeenCheckedエラーが発生しやすい、もろいコードにつながる可能性があります。

親または祖先コンポーネントに直接状態を書き込まないでください。このパターンは、理解が難しく、ExpressionChangedAfterItHasBeenCheckedエラーが発生しやすい、もろいコードにつながる可能性があります。