WESEEK Tech Blog

WESEEK のエンジニアブログです

Angular Tips その2

こちらは 「SEROKUを支える技術 Angular Tips編 その2」からの転載です。

本記事では SEROKU の開発を例に Angular Tips の紹介をしています。(その 3 くらいまで続いています)



やってまいりました、SEROKU を支える技術 Angular Tips その2。

前回の記事執筆からほぼ1ヶ月。待ってくれていた人、そうでない人、いるかとは思いますがめげずにまた書いていこうと思います。

その1でも書きましたが、あくまで、こう書くべきだという強い意志ではなく、こう書いたら良いんじゃないかな? くらいの気持ちで書きますので参考程度にしてらえれば幸いです。

さて、今回は特段前置きは不要かと思いますので(必要あらば前回の記事をサクッと見てください)いきなり書いていこうと思います。

Tips 3: ng-template と ng-container の使い分け

Angular v4 になって、ただの template タグが ng-template にほぼ置き換わるような形になり、angular tepmlate のような文字列でグーグル先生に問い合わせたときに ng-template もしくは ng-container で書かれたサンプルがヒットしたりなんかしてどっち使えばいいか混乱しませんか?(私は結構しました)

混乱の元はなにかといえば、

  • どちらもタグ自身は吐き出される HTML には出力されない
  • ngIf, ngFor 構文が使える(ただし書き方にちと作法がある)
  • ng- の prefix を持っている
  • どちらの書き方をしても見た目には同じ表示になる

といったところでしょうか。ではサンプルソースを元に検証していきたいと思います。

ngIf を ng-template と ng-container で書いてみる

  • ng-template の場合
<ng-template [ngIf]="評価したいもの">
    ・
    ・
    ・
</ng-template>
  • ng-container の場合
<ng-container *ngIf="評価したいもの">
    ・
    ・
    ・
</ng-container>

ngIf の書き方が違うことに注意してください。ng-container の場合は * による ngIf で書けますが、 ng-template の場合はその記法で書くとただのコメントかのような振る舞いになってしまうので注意が必要です。

ngFor を ng-template と ng-container で書いてみる

  • ng-template の場合
<ng-template ngFor let-ローカル変数名 [ngForOf]="forで回したい変数">
    <反復して表示させたいタグ>{{ ローカル変数名でアクセス可能 }}</反復して表示させたいタグ>
</ng-template>
  • ng-containerの場合
<ng-container *ngFor="let ローカル変数名 of forで回したい変数">
    <反復して表示させたいタグ>{{ ローカル変数名でアクセス可能 }}</反復して表示させたいタグ>
</ng-container>

こちらも ngFor(of) の書き方が違うことに注意してください。

単純な書き方の比較だけをすると、ng-container のほうが通常のタグと同じような構文で書けますね。 なので ng-container を使いましょう!笑

と書きましたが、ちょっと説明が短絡的すぎですね(; ・`д・´)

もう少し詳しく書くと

そもそもなのですが、やはり役割が違います笑

単純なクラスの話だけをしても TemplateRefViewContainerRef で分かれていますが、名前の通り ng-template はただのテンプレートとしての機能です。 ngIf/then/else 構文 の説明のときに、then または else に指定したのは、まさにこの ng-templateTepmlateRef そのものだったのです。

一方、 ng-container は then/else には指定できません。ベースのクラスが違うからと言うのが一番の理由ですが、使いみちがやはり違うということでしょう。 ちなみに、ng-template で定義したテンプレートを ng-container の view として出力することができます。コードで見てみましょう。

  • ng-contaier の中身を ng-template で定義したもので表示する
<!-- テンプレートを定義-->
<ng-template #sample>
    <div>
    サンプルのテンプレートです。    
    </div>
</ng-template>

<ng-container *ngTemplateOutlet="sample"></ng-container>

これだけで見たら、ngIf(もしくは ngSwitch )の出し分けで事足りるわけですが、このように ng-template で定義したものを動的に出し分けることが可能なので複雑なロジックが絡む場合はうまく組み合わせると良いかもですね。

別の観点か

実装のお話をもう少しすると、ngIf, ngFor は同一のタグに対しては設定できません。 どういうことかというと、以下のようなコードはエラーになります。

  • ngIf, ngForを書きたい
<div *ngIf="条件" *ngFor="ループ">
・
・
・
</div>

なので以下のように書くこともできますがそうすると、外側に書いた div タグが無駄に出力されてしまいます。

  • ngIf, ngForを書きたいのでタグ分けるサンプル
<div *ngIf="条件">←書けるけどこのdiv本当は不要
    <div *ngFor="ループ">
        ・
        ・
        ・
    </div>
</div>

ここで使えるのが ng-container です。(前述の通りng-templateでも書くことはできますがng-containerで書くことをお勧めします )

  • ngIf, ngForを書きたいのでタグ分けるサンプル( ng-container を使う)
<ng-container *ngIf="条件">
    <div *ngFor="ループ">
        ・
        ・
        ・
    </div>
</ng-container>

こう書くことで出し分けをしつつ、無駄なタグが出力されるのを防げます。

*ngFor の内部に *ngIf のような場面でも活躍しますね。(一定のループ処理で表示したいときに一部分だけ特定の条件下で表示/非表示を切り替えたい時など)

あと、副次的な作用として、 ngIf/ngFor を使うときに ng-container を使うことをルール化すればコードリーディングの観点からも見通しが良いかもしれません。( ng-container が書いてある箇所は何かしらの出し分け処理が挟まっているとパット見でわかる)

以下のようなコードは ngIf, ngFor が div や ul, li タグ、はたまた ng-container で色々使われていてなかなかカオスですね(^^;

脳内変換力が試されるようなコードはなるべく避けましょう笑(数カ月後、自身ですらわからなくなる可能性あり!!)

  • 無秩序な ngIf, ngFor を使ったサンプル
<div *ngIf="条件その1">
    <ul>
        <li *ngFor="ループ"></li>    
    </ul>
</div>
<ul *ngIf="!条件その1">
    <ng-container *ngIf="条件その2">
        <li *ngFor="ループ"></li>    
    </ng-container>
</ul>
<ng-container *ngIf="条件その3">
    ・
    ・
    ・
</ng-container>

まとめ

今回は ng-template, ng-container に関して書きました。

次回は ng-content を使った、テンプレートに対して外部から子要素を突っ込むような処理を書いてみようかと思います。 それでは皆さんごきげんよう

関連記事

tech.weseek.co.jp