序章

単一責任の原則は、アプリケーションの一部に1つの目的があるべきであるという考えです。 この原則に従うと、Angularアプリのテストと開発が簡単になります。

Angularでは、 NgTemplateOutlet 特定のコンポーネントを作成する代わりに、コンポーネント自体を変更することなく、さまざまなユースケースに合わせてコンポーネントを簡単に変更できます。

この記事では、既存のコンポーネントを使用するように書き直します NgTemplateOutlet.

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、Nodev16.6.2で検証されました。 npm v7.20.6、および @angular/core v12.2.0。

ステップ1-構築 CardOrListViewComponent

検討 CardOrListViewComponent 表示されます items'card' または 'list' そのに応じてフォーマット mode.

それはで構成されています card-or-list-view.component.ts ファイル:

card-or-list-view.component.ts
import {
  Component,
  Input
} from '@angular/core';

@Component({
  selector: 'card-or-list-view',
  templateUrl: './card-or-list-view.component.html'
})
export class CardOrListViewComponent {

  @Input() items: {
    header: string,
    content: string
  }[] = [];

  @Input() mode: string = 'card';

}

そして、 card-or-list-view.component.html テンプレート:

card-or-list-view.component.html
<ng-container [ngSwitch]="mode">
  <ng-container *ngSwitchCase="'card'">
    <div *ngFor="let item of items">
      <h1>{{item.header}}</h1>
      <p>{{item.content}}</p>
    </div>
  </ng-container>
  <ul *ngSwitchCase="'list'">
    <li *ngFor="let item of items">
      {{item.header}}: {{item.content}}
    </li>
  </ul>
</ng-container>

このコンポーネントの使用例を次に示します。

使用法.component.ts
import { Component } from '@angular/core';

@Component({
  template: `
    <card-or-list-view
        [items]="items"
        [mode]="mode">
    </card-or-list-view>
`
})
export class UsageExample {
  mode = 'list';
  items = [
    {
      header: 'Creating Reuseable Components with NgTemplateOutlet in Angular',
      content: 'The single responsibility principle...'
    } // ... more items
  ];
}

このコンポーネントには単一の責任はなく、柔軟性もあまりありません。 それはその追跡する必要があります mode 表示する方法を知っています items 両者に cardlist 見る。 そしてそれは表示することができるだけです items とともに headercontent.

テンプレートを使用してコンポーネントを個別のビューに分割することにより、これを変更しましょう。

ステップ2–理解する ng-templateNgTemplateOutlet

許可するために CardOrListViewComponent あらゆる種類の表示 items それらを表示する方法を教えることができる必要があります。 これは、スタンプを押すために使用できるテンプレートを与えることで実現できます。 items.

テンプレートは次のようになります TemplateRefs を使用して <ng-template> スタンプは EmbeddedViewRefs から作成された TemplateRefs. EmbeddedViewRefs 独自のコンテキストでAngularのビューを表し、最小の必須ビルディングブロックです。

Angularは、テンプレートからビューをスタンプアウトするというこの概念を使用する方法を提供します。 NgTemplateOutlet.

NgTemplateOutlet を取るディレクティブです TemplateRef とコンテキストとスタンプアウト EmbeddedViewRef 提供されたコンテキストで。 コンテキストは、テンプレートで次の方法でアクセスされます let-{{templateVariableName}}="contextProperty" テンプレートが使用できる変数を作成するための属性。 コンテキストプロパティ名が指定されていない場合は、 $implicit 財産。

次に例を示します。

import { Component } from '@angular/core';

@Component({
  template: `
    <ng-container *ngTemplateOutlet="templateRef; context: exampleContext"></ng-container>
    <ng-template #templateRef let-default let-other="aContextProperty">
      <div>
        $implicit = '{{default}}'
        aContextProperty = '{{other}}'
      </div>
    </ng-template>
`
})
export class NgTemplateOutletExample {
  exampleContext = {
    $implicit: 'default context property when none specified',
    aContextProperty: 'a context property'
  };
}

例からの出力は次のとおりです。

<div>
  $implicit = 'default context property when none specified'
  aContextProperty = 'a context property'
</div>

The defaultother 変数はによって提供されます let-defaultlet-other="aContextProperty" 小道具。

ステップ3–リファクタリング CardOrListViewComponent

に柔軟性を提供するため CardOrListViewComponent あらゆるタイプの表示を許可します items、テンプレートとして読み込む2つの構造ディレクティブを作成します。 これらのテンプレートは、カードとリストアイテムになります。

ここは card-item.directive.ts:

card-item.directive.ts
import { Directive } from '@angular/core';

@Directive({
  selector: '[cardItem]'
})
export class CardItemDirective {

  constructor() { }

}

そしてここに list-item.directive.ts:

list-item.directive.ts
import { Directive } from '@angular/core';

@Directive({
  selector: '[listItem]'
})
export class ListItemDirective {

  constructor() { }

}

CardOrListViewComponent インポートします CardItemDirectiveListItemDirective:

card-or-list-view.component.ts
import {
  Component,
  ContentChild,
  Input,
  TemplateRef 
} from '@angular/core';
import { CardItemDirective } from './card-item.directive';
import { ListItemDirective } from './list-item.directive';

@Component({
  selector: 'card-or-list-view',
  templateUrl: './card-or-list-view.component.html'
})
export class CardOrListViewComponent {

  @Input() items: {
    header: string,
    content: string
  }[] = [];

  @Input() mode: string = 'card';

  @ContentChild(CardItemDirective, {read: TemplateRef}) cardItemTemplate: any;
  @ContentChild(ListItemDirective, {read: TemplateRef}) listItemTemplate: any;

}

このコードは、構造ディレクティブを次のように読み取ります。 TemplateRefs.

card-or-list-view.component.html
<ng-container [ngSwitch]="mode">
  <ng-container *ngSwitchCase="'card'">
    <ng-container *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="cardItemTemplate"></ng-container>
    </ng-container>
  </ng-container>
  <ul *ngSwitchCase="'list'">
    <li *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="listItemTemplate"></ng-container>
    </li>
  </ul>
</ng-container>

このコンポーネントの使用例を次に示します。

使用法.component.ts
import { Component } from '@angular/core';

@Component({
  template: `
    <card-or-list-view
        [items]="items"
        [mode]="mode">
      <div *cardItem>
        Static Card Template
      </div>
      <li *listItem>
        Static List Template
      </li>
    </card-or-list-view>
`
})
export class UsageExample {
  mode = 'list';
  items = [
    {
      header: 'Creating Reuseable Components with NgTemplateOutlet in Angular',
      content: 'The single responsibility principle...'
    } // ... more items
  ];
}

これらの変更により、 CardOrListViewComponent 提供されたテンプレートに基づいて、カードまたはリストフォームに任意のタイプのアイテムを表示できるようになりました。 現在、テンプレートは静的です。

最後に行う必要があるのは、コンテキストを与えることでテンプレートを動的にすることです。

card-or-list-view.component.html
<ng-container [ngSwitch]="mode">
  <ng-container *ngSwitchCase="'card'">
    <ng-container *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="cardItemTemplate; context: {$implicit: item}"></ng-container>
    </ng-container>
  </ng-container>
  <ul *ngSwitchCase="'list'">
    <li *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="listItemTemplate; context: {$implicit: item}"></ng-container>
    </li>
  </ul>
</ng-container>

このコンポーネントの使用例を次に示します。

使用法.component.ts
import { Component } from '@angular/core';

@Component({
  template: `
    <card-or-list-view
        [items]="items"
        [mode]="mode">
      <div *cardItem="let item">
        <h1>{{item.header}}</h1>
        <p>{{item.content}}</p>
      </div>
      <li *listItem="let item">
        {{item.header}}: {{item.content}}
      </li>
    </card-or-list-view>
`
})
export class UsageExample {
  mode = 'list';
  items = [
    {
      header: 'Creating Reuseable Components with NgTemplateOutlet in Angular',
      content: 'The single responsibility principle...'
    } // ... more items
  ];
}

注目すべき興味深い点は、構文糖衣にアスタリスクプレフィックスとmicrosyntaxを使用していることです。 それは次と同じです:

<ng-template cardItem let-item>
  <div>
    <h1>{{item.header}}</h1>
    <p>{{item.content}}</p>
  </div>
</ng-template>

以上です! 元の機能はありますが、テンプレートと CardOrListViewComponent 責任が少ない。 次のようなアイテムコンテキストにさらに追加できます first また last に似ている ngFor または完全に異なるタイプを表示します items.

結論

この記事では、既存のコンポーネントを取得して、使用するために書き直しました NgTemplateOutlet.

Angularについて詳しく知りたい場合は、Angularトピックページで演習とプログラミングプロジェクトを確認してください。