angular.io Guide: Structual Directives

これは、Angular の公式ドキュメントの [Structural Directives](https://angular.io/guide/structural-directives) の章 を意訳したものです｡
駆け足で翻訳したので至らない点もありますが、あしからずご承知おきください｡

バージョン 4.2.6 のドキュメントをベースにしています｡

# Structural Directives

このガイドでは、Angularの構造ディレクティブを使用してDOMを操作する方法と、同じことを行う独自の構造ディレクティブを作成する方法について説明します。

ここから、Structural Directive の [example を見たり](https://angular.io/generated/live-examples/structural-directives/eplnkr.html)、[ダウンロード](https://angular.io/generated/zips/structural-directives/structural-directives.zip) ができます｡

## 構造ディレクティブとは
構造ディレクティブはHTMLレイアウトを担当します。通常、要素の追加、削除、または操作によって、DOMの構造を整形または変形します｡

他のディレクティブと同様に、構造ディレクティブをホスト要素に適用します。この指令は、そのホスト要素とその子孫で何が行われるべきかを行います。

構造ディレクティブは認識しやすいです。この例のように、ディレクティブ属性名の前には、アスタリスク`(*)` が付いています。

*src/app/app.component.html (ngif)*

```
<div *ngIf="hero" >{{hero.name}}</div>
```

[] かっこはありません。() かっこもありません。`*ngIf` 文字列で設定します。
このガイドでは、アスタリスク(*)は便利な表記であり、文字列は通常のテンプレート式ではなく、マイクロシンタックスであることを学習します。

Angularはこの記法をホスト要素とその子孫を囲むマークアップされた `<ng-template>` に加えます。各構造ディレクティブは、そのテンプレートとは何か異なっています。

`NgIf`、`NgFor`、`NgSwitch` ...の3つの一般的な組み込み構造ディレクティブは、テンプレート構文ガイドで説明されており、Angularドキュメント全体のサンプルにも記載されています。
テンプレートの例を以下に記します｡

*src/app/app.component.html (built-in)*

```
<div *ngIf="hero" >{{hero.name}}</div>

<ul>
  <li *ngFor="let hero of heroes">{{hero.name}}</li>
</ul>

<div [ngSwitch]="hero?.emotion">
  <happy-hero    *ngSwitchCase="'happy'"    [hero]="hero"></happy-hero>
  <sad-hero      *ngSwitchCase="'sad'"      [hero]="hero"></sad-hero>
  <confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero>
  <unknown-hero  *ngSwitchDefault           [hero]="hero"></unknown-hero>
</div>
```

このガイドでは、これらの使用方法は繰り返されません。しかし、彼らはどのように動作し、独自の構造ディレクティブあを書くか説明しています。

#### ディレクティブのスペル
このガイドでは、UpperCamelCase と lowerCamelCaseの両方で綴られた指示文が表示されます。すでにあなたは `NgIf` と `ngIf` を見たことがありますよね。理由があります。` NgIf` はディレクティブクラスを指します。 `ngIf` は、ディレクティブの属性名を参照します。

指示クラスはUpperCamelCase（NgIf）で記述されています。ディレクティブの属性名はlowerCamelCase（ngIf）に記載されています。
このガイドは、そのプロパティとディレクティブが何をしているかについて話すときに、ディレクティブクラスを参照します。このガイドでは、HTMLテンプレートの要素にディレクティブをどのように適用するかを記述する際に、属性名を参照しています。

> Angularディレクティブには、（1）コンポーネントと（2）属性ディレクティブの2つの種類があります。
> コンポーネントは、HTMLの領域をネイティブHTML要素の方法で管理します。技術的にはテンプレート付きのディレクティブです。
> 属性ディレクティブは、要素、コンポーネント、または別のディレクティブの外観や動作を変更します。たとえば、組み込みのNgStyleディレクティブは、同時に複数の要素スタイルを変更します。
> 1つのホスト要素に多くの属性ディレクティブを適用できます。 1つの構造ディレクティブのみをホスト要素に適用できます。

## NgIf のケーススタディ
NgIfは最も単純な構造ディレクティブであり、理解しやすいものです。これはboolean式の結果に応じて、DOMの全体のかたまりを表示または削除します｡

*src/app/app.component.html (ngif-true)*

```
<p *ngIf="true">
  Expression is true and ngIf is true.
  This paragraph is in the DOM.
</p>
<p *ngIf="false">
  Expression is false and ngIf is false.
  This paragraph is not in the DOM.
</p>
```

`ngIf` ディレクティブはCSSで要素を隠すわけではありません。それらを物理的にDOMから追加および削除します。ブラウザ開発ツールを使用してその事実を確認し、DOMをチェックしてみましょう。

![ngIf=false element not in DOM](https://angular.io/generated/images/guide/structural-directives/element-not-in-dom.png)

一番上の段落はDOMにあります。下の、廃止された段落はありません。その場所には "バインディング"についてのコメントがあります（詳細は[後述](https://angular.io/guide/structural-directives#asterisk)します）。

条件が`false`の場合、`NgIf`はDOMからホスト要素を削除し、DOMイベント（作成した添付ファイル）からそのホスト要素を切り離し、Angular change detectionからコンポーネントを切り離して破棄します。
コンポーネントとDOMノードはガーベージコレクションされ、メモリが解放されます。

#### なぜDOMを非表示にするのではなく削除するのですか？
ディレクティブは、表示スタイルを `none` に設定することによって、代わりに不要な段落を非表示にすることができます。

*src/app/app.component.html (display-none)*

```
<p [style.display]="'block'">
  Expression sets display to "block".
  This paragraph is visible.
</p>
<p [style.display]="'none'">
  Expression sets display to "none".
  This paragraph is hidden but still in the DOM.
</p>
```

不可視の間、要素はDOMに残ります。

![hidden element still in DOM](https://angular.io/generated/images/guide/structural-directives/element-display-in-dom.png)

非表示と削除の違いは、このようなシンプルな`<p>`要素での使用は問題になりません。ホスト要素がリソース集約型コンポーネントに接続されているときも問題ありません。
このようなコンポーネントの動作は、非表示になっても継続されます。コンポーネントは、そのDOM要素に接続されたままです。接続されたままの状態は、イベントをListenし続けます。 Angularは、データバインディングに影響する可能性のある変更をチェックし続けます。コンポーネントが何をしていたとしても、Listenを続けます。

非表示状態は、コンポーネントとその子孫コンポーネントのすべてがリソースを結び付いています。パフォーマンスとメモリの負担が大きくなり、応答性が低下するにもかかわらず、ユーザーには何も表示されません。

メリットとしては、要素を再び表示するのは迅速に動作します。コンポーネントの以前の状態は保持され、表示する準備ができているからです。コンポーネントは再初期化されません。コンポーネントの初期化はコストの高い操作です。だから非表示／表示の切り替えは時々正しいアプローチにもなり得ます｡

しかし、忘れてはならない明快な理由がない場合、ユーザが見ることができないDOM要素を削除し、NgIfのような構造ディレクティブを使って未使用のリソースを開放することが望ましいでしょう。

これらの同じ考慮事項は、組み込み／カスタムにかかわらず、すべての構造ディレクティブに適用されます。構造ディレクティブを適用する前に、要素の追加と削除、およびコンポーネントの作成と破棄の結果を、一度立ち止まって考えることをお勧めします。

### アスタリスク (*) 接頭辞

確かに、ディレクティブ名の前にアスタリスク(*)が付いていることに気付き、それがなぜ必要なのか、それが何であるのか疑問に思いませんでしたか｡
ヒーローが存在する場合、ヒーローの名前を表示する `*ngIf` です。

*src/app/app.component.html (asterisk)*

```
<div *ngIf="hero" >{{hero.name}}</div>
```

アスタリスクはもう少し複雑なもののための「シンタックスシュガー」です。内部的に、Angularは2段階でそれを処理します。
まず、`*ngIf="..."` をテンプレート属性 `template="ngIf ..."` に変換します。

*src/app/app.component.html (ngif-template-attr)*

```
<div template="ngIf hero">{{hero.name}}</div>
```

次に、テンプレート属性を `<ng-template>` 要素に変換します。これは、このようにホスト要素を囲んでいます。

*src/app/app.component.html (ngif-template)*

```
<ng-template [ngIf]="hero">
  <div>{{hero.name}}</div>
</ng-template>
```

- `*ngIf` ディレクティブは `<ng-template>` 要素に移動し、プロパティバインディング`[ngIf]` になりました。
- クラス属性を含む残りの `<div>` は、`<ng-template>` 要素内に移動しました。

これらのフォームは実際にレンダリングされません。完成形のみがDOMにレンダリングされて終了します。

![hero div in DOM](https://angular.io/generated/images/guide/structural-directives/hero-div-in-dom.png)

Angularは、実際のレンダリング中に `<ng-template>` コンテンツをレンダリングし、`<ng-template>` を判定結果コメントに置き換えました。

`NgFor` と `NgSwitch` ... ディレクティブも、同じパターンに従います。

## `*ngFor` の裏側

Angularは `*ngFor` を同様の方法でアスタリスク（*）構文からテンプレート属性を介して`<ng-template>` 要素に変換します。

`NgFor` のフル機能のアプリケーションは、次の3つの方法で書かれています。

*src/app/app.component.html (inside-ngfor)*

```
<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
  ({{i}}) {{hero.name}}
</div>

<div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
  ({{i}}) {{hero.name}}
</div>

<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
  <div [class.odd]="odd">({{i}}) {{hero.name}}</div>
</ng-template>
```

これは、`ngIf` よりも明らかに複雑であり、正しいことです。 `NgFor` ディレクティブには、このガイドに記載されている `NgIf` より多くの機能（必須およびオプション）があります。少なくとも `NgFor` ではループ変数（`let hero`）とリスト（`heroes`）が必要です。

ngForに割り当てられた文字列でこれらの機能を有効にします。これはAngularのマイクロシンタクスで記述します。

> `ngFor` 文字列の外側のすべては、`<ng-template>` 内を移動するときにホスト要素（`<div>`）と一緒にとどまります。この例では、`[ngClass]="odd"` は `<div>` にとどまります。

### マイクロシンタックス
Angular microsyntax を使用すると、コンパクトでフレンドリーな文字列でディレクティブを設定できます。 microsyntax パーサーは、その文字列を `<ng-template>` の属性に変換します。

- `let` キーワードは、テンプレート内で参照するテンプレート入力変数を宣言します。 この例の入力変数は、ヒーロー、i、および奇数です。 パーサはlet hero、let i、let-hero、let-i、let-oddという名前の変数にlet letを翻訳します。

- microsyntaxパーサーは、それらを（`of` → `Of`、`trackBy` → `TrackBy`）タイトルケースに取り込み、ディレクティブの属性名（`ngFor`）の前に `ngForOf` および `ngForTrackBy` という名前を付けます。それらは2つの `NgFor` 入力プロパティの名前です。それがディレクティブがリストが `heroes` であり、track-by関数がtrackByIdであることを知る方法です。

- NgForディレクティブはリストをループするので、独自のコンテキストオブジェクトのプロパティを設定およびリセットします。これらのプロパティには、indexとoddと、`$implicit` という特別なプロパティがあります。

- let-iとlet-odd変数は、`let i = index` と `let odd = odd` と定義されていました。 Angularは、それらをコンテキストのインデックスと奇妙なプロパティの現在の値に設定します。
- let-hero のコンテキストプロパティは指定されていません。それは暗黙のソースです。 Angularは、NgForが現在のヒーローのリピートで初期化したコンテキストの `$implicit` なプロパティの値をlet-heroに設定します。

- APIガイドでは、追加のNgForディレクティブプロパティとコンテキストプロパティについて説明します。

これらのマイクロシンタックスメカニズムは、独自の構造指令を記述するときに利用できます。 NgIfとNgForのソースコードを学ぶことは、もっと学ぶための素晴らしい方法です。

#### テンプレート input 変数

テンプレート入力変数は、その値がテンプレートの単一インスタンス内で参照できる変数です。この例にはいくつかの変数があります：hero、i、odd。すべてにはletというキーワードが先行しています。

テンプレート入力変数は、意味的にも構文的にもテンプレート参照変数と同じではありません。

letキーワード（let hero）を使用して、テンプレート入力変数を宣言します。変数のスコープは、繰り返されるテンプレートの1つのインスタンスに限定されます。他の構造ディレクティブの定義で同じ変数名を使用することができます。

＃（＃var）を変数名の前に付けて、テンプレート参照変数を宣言します。参照変数は、その添付された要素、コンポーネント、または指令を参照します。テンプレート全体のどこにでもアクセスできます。

テンプレート入力変数と参照変数名には固有の名前空間があります。 let heroの英雄は、#heroと宣言された英雄と決して同じ変数ではありません。

#### ホスト要素ごとに1つの構造ディレクティブ

いつか特定の条件が満たされているときだけ、HTMLのブロックを繰り返すことを望みます。 `*ngFor`と`*ngIf`の両方を同じホスト要素に入れようとします。それをAngularは受け入れません。要素に構造指示文を1つだけ適用することができます。

理由は簡単です。構造ディレクティブは、ホスト要素およびその子孫と複雑な処理を行うことができます。 2つのディレクティブが同じホスト要素に優先順位をつけている場合、どちらが優先されますか？

NgIfとNgForのどちらが先に進むべきですか？ NgForがNgForの効果をキャンセルできますか？そうであれば、Angularは他の構造ディレクティブを取り消す能力をどのように一般化するべきですか？

これらの質問に対する簡単な答えはありません。複数の構造ディレクティブを禁止すると、それが問題になります。このユースケースには簡単な解決策があります：`*ngFor`を`* ngFor`要素をラップするコンテナ要素に置きましょう。

1つまたは両方の要素がng-containerであるため、余分なレベルのHTMLを導入する必要はありません。

## NgSwitch ディレクティブの裏側

Angular NgSwitchは、実際にはNgSwitch、NgSwitchCase、およびNgSwitchDefaultの3
つが連携して動いているディレクティブのセットです。

ここに例があります。

*src/app/app.component.html (ngswitch)*

```
<div [ngSwitch]="hero?.emotion">
  <happy-hero    *ngSwitchCase="'happy'"    [hero]="hero"></happy-hero>
  <sad-hero      *ngSwitchCase="'sad'"      [hero]="hero"></sad-hero>
  <confused-hero *ngSwitchCase="'confused'" [hero]="hero"></confused-hero>
  <unknown-hero  *ngSwitchDefault           [hero]="hero"></unknown-hero>
</div>
```

NgSwitch（hero.emotion）に割り当てられたスイッチ値は、スイッチケースのどれが表示されるかを決定します。

NgSwitch自体は構造指令ではありません。他の2つのスイッチ指令の動作を制御する属性指令です。だからあなたは[ngSwitch]、決して* ngSwitchと書くのです。

NgSwitchCaseとNgSwitchDefaultは構造ディレクティブです。アスタリスク（*）接頭辞表記を使用して要素にアタッチします。
NgSwitchCaseは、その値がスイッチの値と一致すると、そのホスト要素を表示します。 NgSwitchDefaultは、兄弟NgSwitchCaseがスイッチの値と一致しない場合、そのホスト要素を表示します。

> ディレクティブを適用する要素は、そのホスト要素です。 `<happy-hero>` は、`* ngSwitchCase` のホスト要素です。 `<unknown-hero>` は、`*ngSwitchDefault` のホスト要素です。

他の構造ディレクティブと同様に、NgSwitchCaseとNgSwitchDefaultをテンプレート属性フォームに組み込むことができます。

*src/app/app.component.html (ngswitch-template-attr)*

```
<div [ngSwitch]="hero?.emotion">
  <happy-hero    template="ngSwitchCase 'happy'"    [hero]="hero"></happy-hero>
  <sad-hero      template="ngSwitchCase 'sad'"      [hero]="hero"></sad-hero>
  <confused-hero template="ngSwitchCase 'confused'" [hero]="hero"></confused-hero>
  <unknown-hero  template="ngSwitchDefault"         [hero]="hero"></unknown-hero>
</div>
```

これは、`<ng-template>` 要素の形式に変更することができます。

*src/app/app.component.html (ngswitch-template)*

```
<div [ngSwitch]="hero?.emotion">
  <ng-template [ngSwitchCase]="'happy'">
    <happy-hero [hero]="hero"></happy-hero>
  </ng-template>
  <ng-template [ngSwitchCase]="'sad'">
    <sad-hero [hero]="hero"></sad-hero>
  </ng-template>
  <ng-template [ngSwitchCase]="'confused'">
    <confused-hero [hero]="hero"></confused-hero>
  </ng-template >
  <ng-template ngSwitchDefault>
    <unknown-hero [hero]="hero"></unknown-hero>
  </ng-template>
</div>
```

## アスタリスク(*) の構文を使う

アスタリスク `(*)` の構文は、他のdesugaredフォームよりも明らかです。ディレクティブをホストする要素が1つもない場合は、`<ng-container>` を使用します。

テンプレートの属性や要素の形式で構造指示文を適用することはほとんどありませんが、<ng-template>を作成し、それがどのように動作するかを理解することは重要です。構造ディレクティブを書くときに<ng-template>を参照します。

## `<ng-template>` について

<ng-template>は、HTMLをレンダリングするためのAngular要素です。直接表示されることはありません。実際、ビューをレンダリングする前に、Angularは<ng-template>とその内容をコメントで置き換えます。

構造指示文がなく、<ng-template>にいくつかの要素を単にラップすると、それらの要素は消えます。それは真ん中の運命だよ "ヒップ！"フレーズで "Hip！Hip！Hooray！"

*src/app/app.component.html (template-tag)*

```
<p>Hip!</p>
<ng-template>
  <p>Hip!</p>
</ng-template>
<p>Hooray!</p>
```

Angularは中央の "Hip！"を消し、応援はやや熱狂的である。

![template tag rendering](https://angular.io/generated/images/guide/structural-directives/template-rendering.png)

構造ディレクティブは、独自の構造ディレクティブを記述するときに表示されるように `<ng-template>` を動作させます。

## `<ng-container>` を持つ兄弟要素をグループ化する

多くの場合、構造命令をホストすることができるルート要素が必要です。リスト要素（<li>）は、NgForリピータの典型的なホスト要素です。

*src/app/app.component.html (ngfor-li)*

```
<li *ngFor="let hero of heroes">{{hero.name}}</li>
```

ホスト要素がない場合は、通常<div>などのネイティブHTMLコンテナ要素にコンテンツをラップし、そのラッパーにディレクティブを添付できます。

*src/app/app.component.html (ngif)*

```
<div *ngIf="hero" >{{hero.name}}</div>
```

別のコンテナ要素（通常は<span>または<div>）を導入して、単一のルートの下に要素をグループ化するのは通常無害です。通常は...必ずしもそうではありません。

CSSスタイルは新しいレイアウトを期待したり受け入れたりしないため、グループ化要素によってテンプレートの外観が損なわれる可能性があります。たとえば、次の段落レイアウトがあるとします。

*src/app/app.component.html (ngif-span)*

```
<p>
  I turned the corner
  <span *ngIf="hero">
    and saw {{hero.name}}. I waved
  </span>
  and continued on my way.
</p>
```

`<p>` 段落内の `<span>` に適用されるCSSスタイルルールもあります。

*src/app/app.component.css (p-span)*

```
p span { color: red; font-size: 70%; }
```

構成された段落が不思議とレンダリングされます。

![spanned paragraph with bad style](https://angular.io/generated/images/guide/structural-directives/bad-paragraph.png)

他の場所での使用を意図したpスパンスタイルは、ここで誤って適用されていました。

別の問題：一部のHTML要素では、すべての直接の子どもが特定のタイプである必要があります。たとえば、`<select>` 要素には`<option>` 子が必要です。条件付きの`<div>` または`<span>` にオプションをラップすることはできません。

ではこれを試してみましょう:

*src/app/app.component.html (select-span)*

```
<div>
  Pick your favorite hero
  (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>)
</div>
<select [(ngModel)]="hero">
  <span *ngFor="let h of heroes">
    <span *ngIf="showSad || h.emotion !== 'sad'">
      <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
    </span>
  </span>
</select>
```

ドロップダウンは空です。

![spanned options don't work](https://angular.io/generated/images/guide/structural-directives/bad-select.png)

ブラウザは `<span>` 内に `<option>` を表示しません。

### `<ng-container>` をレスキューに

Angular `<ng-container>` は、AngularがDOMに配置しないため、スタイルやレイアウトを妨げないグループ化要素です。
今度は `<ng-container>` を使う条件付きの段落です。

*src/app/app.component.html (ngif-ngcontainer)*

```
<p>
  I turned the corner
  <ng-container *ngIf="hero">
    and saw {{hero.name}}. I waved
  </ng-container>
  and continued on my way.
</p>
```

これは正しくレンダリングされます。

![ngcontainer paragraph with proper style](https://angular.io/generated/images/guide/structural-directives/good-paragraph.png)

今度は `<ng-container>` で select `<option>` を条件付きで除外します。

*src/app/app.component.html (select-ngcontainer)*

```
<div>
  Pick your favorite hero
  (<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>)
</div>
<select [(ngModel)]="hero">
  <ng-container *ngFor="let h of heroes">
    <ng-container *ngIf="showSad || h.emotion !== 'sad'">
      <option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
    </ng-container>
  </ng-container>
</select>
```

ドロップダウンは適切に動作します。

![ngcontainer options work properly](https://angular.io/generated/images/guide/structural-directives/select-ngcontainer-anim.gif)

`<ng-container>` は、Angularパーサーによって認識される構文要素です。これは、ディレクティブやコンポーネント、クラス、インタフェースではありません。 JavaScript の `if` ブロックの中かっこ内とよく似ています。

```
if (someCondition) {
  statement1;
  statement2;
  statement3;
}
```

これらの中カッコがなければ、条件付きですべてを単一のブロックとして実行するときにJavaScriptは最初のステートメントのみを実行します。 `<ng-container>` はAngularテンプレートの同様の必要性を満たします。

## 構造ディレクティブを書く

このセクションでは、`NgIf` とは逆の `UnlessDirective` 構造ディレクティブを記述します。条件が`true`である場合、`NgIf` はテンプレートのコンテンツを表示します。条件が`false`の場合は、コンテンツが表示されます。

*src/app/app.component.html (myUnless-1)*

```
<p *myUnless="condition">Show this sentence unless the condition is true.</p>
```

ディレクティブの作成は、コンポーネントの作成に似ています。

- Directiveデコレータをインポートします（Componentデコレータではなく）。
- Input、TemplateRef、およびViewContainerRefシンボルをインポートします。あなたは構造的な指示にそれらを必要とします。
- ディレクティブクラスにデコレータを適用します。
- テンプレートの要素に適用されたときにディレクティブを識別するCSS属性セレクタを設定します。

どのように開始するかは次のとおりです。

*src/app/unless.directive.ts (skeleton)*

```
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[myUnless]'})
export class UnlessDirective {
}
```

ディレクティブのセレクタは通常、ディレクティブの属性名を角括弧 `[myUnless]` で囲みます。角括弧はCSS属性セレクタを定義します。

ディレクティブの属性名は、lowerCamelCase で記述し、接頭辞で始まる必要があります。 `ng` を使用しないでください。そのプレフィックスは Angular に属します。あなたのプロジェクトや会社に合った、短いものを選んでください。この例では、接頭辞は `my` です。

ディレクティブクラス名は、スタイルガイドに従ってディレクティブで終わります。 Angularの独自のディレクティブはそうではありません。

### TemplateRef と ViewContainerRef

このような単純な構造ディレクティブは、Angular生成の `<ng-template>` から埋め込みビューを作成し、ディレクティブの元の `<p>` ホスト要素に隣接するビューコンテナにそのビューを挿入します。

`TemplateRef` で `<ng-template>` の内容を取得し、`ViewContainerRef` を介してビューコンテナにアクセスします。

クラスの private 変数としてディレクティブコンストラクタに両方を注入します。

*rc/app/unless.directive.ts (ctor)*

```
constructor(
  private templateRef: TemplateRef<any>,
  private viewContainer: ViewContainerRef) { }
```

### myUnless プロパティ

ディレクティブの消費者は、`true` / `false` 条件を `[myUnless]` にバインドすることを想定しています。つまり、ディレクティブには `@Input` で装飾された `myUnless` プロパティが必要です。

> `@Input` については、[テンプレート構文ガイド](https://angular.io/guide/template-syntax#inputs-outputs)を参照してください。

*src/app/unless.directive.ts (set)*

```
@Input() set myUnless(condition: boolean) {
  if (!condition && !this.hasView) {
    this.viewContainer.createEmbeddedView(this.templateRef);
    this.hasView = true;
  } else if (condition && this.hasView) {
    this.viewContainer.clear();
    this.hasView = false;
  }
}
```

Angularは、条件の値が変更されるたびに `myUnless` プロパティを設定します。 `myUnless` プロパティは機能するので、セッターが必要です。

- 条件が `false` であり、ビューが以前に作成されていない場合は、テンプレートから埋め込みビューを作成するようにビューコンテナに指示します。
- 条件が `true` で、ビューが現在表示されている場合は、ビューを破棄するコンテナをクリアします。

誰も `myUnless` プロパティを読み込まないので、ゲッターは必要ありません。

完成したディレクティブコードは次のようになります。

*src/app/unless.directive.ts (抜粋)*

```
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

/**
 * Add the template content to the DOM unless the condition is true.
 */
@Directive({ selector: '[myUnless]'})
export class UnlessDirective {
  private hasView = false;

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef) { }

  @Input() set myUnless(condition: boolean) {
    if (!condition && !this.hasView) {
      this.viewContainer.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (condition && this.hasView) {
      this.viewContainer.clear();
      this.hasView = false;
    }
  }
}
```

このディレクティブをAppModuleの宣言配列に追加します。

次に、HTMLを作成して試してみてください。

*src/app/app.component.html (myUnless)*

```
<p *myUnless="condition" class="unless a">
  (A) This paragraph is displayed because the condition is false.
</p>

<p *myUnless="!condition" class="unless b">
  (B) Although the condition is true,
  this paragraph is displayed because myUnless is set to false.
</p>
```

条件が `true` であれば、上部（A）の段落が表示され、下部（B）の段落は消えます。条件が `false` の場合は、上（A）段落が削除され、下（B）段落が表示されます。

![UnlessDirective in action](https://angular.io/generated/images/guide/structural-directives/unless-anim.gif)

## まとめ

完成した[サンプル](https://angular.io/generated/live-examples/structural-directives/eplnkr.html)/[ダウンロード](https://angular.io/generated/zips/structural-directives/structural-directives.zip)の例はこちらからどうぞ｡ `src/app/` フォルダからのソースです。

*（ソースコードは省略）*

今回学んだことは、以下のとおりです

- 構造ディレクティブはHTMLレイアウトを操作します。
- 適切なホスト要素がない場合、`<ng-container>`をグループ化要素として使用します。
- Angular desesterのアスタリスク`（*）`構文を `<ng-template>` に追加する必要があります。
- `NgIf`、`NgFor`、`NgSwitch` の組み込みディレクティブでどのように動作しますか。
- `<ng-template>` に展開される microsyntax について
- カスタム構造ディレクティブ `UnlessDirective` を記述してみました｡