ディレクティブにおけるテンプレート変数機能の実装

画面をロード中に既定の文章などを表示できる、NgIfを参考にして強化版NgIfのような以下のディレクティブ(コンポーネント)を作成しました。

NgIfと同じように使いたかったのですが、評価式をテンプレート変数(;let hogeみたいなやつ)にする機能の実装方法がわかりませんでした。

上のコンポーネントを以下のように利用できるようにするにはどのように実装すればよいでしょうか。

<!-- numが0のときには Loading...などと表示される -->
<div *loading="num * 100; let result">
  {{ result }}%
</div>
1 Like

let hoge に何か値を入れる実装は↓のようになります(機能的にはなんか違うかなーと思ったのですが、ひとまず最もシンプルな実装)。

*loadingみたいなやつはstrcutual directiveで、例に書いてもらったテンプレートは↓のように展開されます。

<ng-template [loading]="num * 100" let-result>
  <div>{{ result }}%</div>
</ng-template>

これはdirectiveでTemplateRefとしてinjectできるのでViewContainerRef#createEmbeddedViewEmbeddedViewRefを作るときに大2引数に{ $implicit: value }と指定してもらうか、すでにある場合はview.context.$implicit = valueというふうに更新してもらうとresultに反映されます(コードを見てもらったほうがわかりやすいかもしれないです)。

export interface LoadingDirectiveContext {
  $implicit: any;
  label: string;
}

@Directive({
  selector: '[loading]'
})
export class LoadingDirective {

  constructor(private viewContainerRef: ViewContainerRef, private templateRef: TemplateRef<LoadingDirectiveContext>) { }

  view: EmbeddedViewRef<LoadingDirectiveContext>;

  @Input() set loading(n: number) {
    if (!this.view) {
      this.view = this.viewContainerRef.createEmbeddedView(this.templateRef, { $implicit: n, label: "hello" });
    } else {
      this.view.context.$implicit = n;
    }
  }
}

context指定のルールに関しては https://angular.io/api/common/NgTemplateOutlet あたりみるとなんとなくわかると思います。

2 Likes

EmbeddedViewRefの第二引数にコンテキスト渡せるんですね!
というわけで以下のようにして実現できました!

今回はテンプレートがある状態の時にコンテキスト渡す必要がなかったので、 view.context.$implicit は利用しませんでしたが、こちらの情報も非常に参考になりました!!

ありがとうございます!!