更新:
おそらく正しい質問は、「自分のバージョンのng-container
を実装するには、「<my-component ...>...</my-component>
」の代わりに「<ng-container my-directive ...>...</ng-container>
」を使用できるようにする方法」です。
私はAngular 4.で新しいアプリを開発しています。ラッパーコンポーネントを使用してアプリ全体を構築したいので、必要に応じて実際のコンポーネントを交換できます。これは単純なコントロールでは簡単に機能しますが、個別のコンポーネントに分割する複雑なコントロールです。要件がかなり安定しているため、最も良い例はタブです。ラベル付きのタブのリストと、表示/非表示にするコンテンツパネルです。
ng-bootstrapは次のようなものを使用できます:
<ngb-tabset>
<ngb-tab title="Tab 1">
<ng-template ngbTabContent>...</ng-template>
</ngb-tab>
...
<ngb-tabset>
このような他のコンポーネントがあるかもしれません:
<div class="my-tab-group">
<ul>
<li>Tab 1</li>
...
</ul>
<div class="my-tab" title="Tab 1">...</div>
...
</div>
現時点でアプリを特定の実装に関連付けるのではなく、独自のタブラッパーを定義して、次のようにあらゆる場所で使用したいと思います。
<my-tabs-control>
<my-tab-control title="Tab 1">...</my-tab-control>
...
<my-tabs-control>
次に、タブの動作を変更する必要がある場合は、1か所で行われます。ただし、ラッパーコンポーネントを使用する場合、HTMLはHost要素タグで汚染され、必要なタグとインタレースされます。
<my-tabs-control>
<div class="my-tab-group">
<ul>
<li>Tab 1</li>
...
</ul>
<my-tab-control title="Tab 1">
<div class="my-tab" title="Tab 1">...</div>
</my-tab-control>
...
</div>
<my-tabs-control>
これが多くの人がAngularコンポーネントのHost要素をアンラップ/削除する方法を尋ねる理由です-これにより、これは非常に簡単になります。
通常の答えは、代わりに属性セレクターを使用することです。例:
angular2コンポーネントのセレクタータグをHTMLから削除/置換する方法
ただし、これは、使用サイトのタグ構造を知る必要があることを意味します。これにより、適切に名前が付けられたラッパータグを使用するポイント全体が明らかに破壊されます。
したがって、最後に、これをどのように行うべきですか?パフォーマンス上の理由により、それはおそらく不可能ですか?私はレンダラー2を検討し始めましたが、私がやりたいことを実行する明確な方法を見つけていません。また、非角度のDOMハックを避けたいです。
うまくいく解決策がありますが、思ったほどエレガントではありません。私は、「ホスト要素を取り除く」というオプションの方がずっとすっきりしているので、はるかに好みます。
このソリューションは、ng-bootstrapプロジェクトでタブがどのように実装されているかを単に確認することから派生しました: https://github.com/ng-bootstrap/ng-bootstrap 。
解決策は、メインコンポーネントとして@Componentを使用し、すべての内部の子コンポーネントに@Directivesを使用することです。通常、他の要素へのアドオンとしてディレクティブを使用しますが、ここでは実際の要素としてディレクティブを使用します。これは、独自のコンテンツをレンダリングせず、独自のホストコンテナを作成しないコンポーネントのように機能するようです。代わりに、ホストコンポーネントがディレクティブのコンテンツをどうするかを決定できます。追加の条件は、ng-templatesを使用する必要があることです。ng-templatesを使用せずにディレクティブコンテンツを直接投影する方法がわかりません。
これが、ラッパーコンポーネントを使用してタブコンテナーをマークアップする方法です。
<my-tab-container>
<my-tab title="Tab 1">
<ng-template myTabContent>
This is panel 1!
</ng-template>
</my-tab>
<my-tab title="Tab 2">
<ng-template myTabContent>
This is panel 2!
</ng-template>
</my-tab>
<my-tab title="Tab 3">
<ng-template myTabContent>
This is panel 3!
</ng-template>
</my-tab>
</my-tab-container>
これが、私のタブラッパーコンポーネントの最小限の実装です。これには、個々のタブとタブコンテンツの2つのディレクティブが含まれています。
import { Component, OnInit, Directive, ContentChildren, QueryList, Input, TemplateRef, ContentChild } from '@angular/core';
@Directive({ selector: 'ng-template[myTabContent]' })
export class TabContentDirective {
constructor(public templateRef: TemplateRef<any>) { }
}
@Directive({ selector: 'my-tab' })
export class TabDirective {
@Input()
title: string;
@ContentChild(TabContentDirective)
content: TabContentDirective;
public getTemplateRef() {
return this.content.templateRef;
}
}
@Component({
selector: 'my-tab-container',
templateUrl: './tab-container.component.html',
styleUrls: ['./tab-container.component.scss']
})
export class TabContainerComponent implements OnInit {
@ContentChildren(TabDirective)
tabs: QueryList<TabDirective>;
constructor() { }
ngOnInit() {
}
}
これで、タブコンテナーコンポーネントのHTMLテンプレートを切り替えることで、さまざまな種類のタブをレンダリングできます。
例えばng-bootstrapの場合:
<ngb-tabset>
<ngb-tab *ngFor="let tab of tabs">
<ng-template ngbTabTitle>{{tab.title}}</ng-template>
<ng-template ngbTabContent>
<ng-template [ngTemplateOutlet]="tab.content.templateRef"></ng-template>
</ng-template>
</ngb-tab>
</ngb-tabset>
例えばAngular Material2:
<md-tab-group>
<md-tab *ngFor="let tab of tabs" label="{{tab.title}}">
<ng-template [ngTemplateOutlet]="tab.content.templateRef"></ng-template>
</md-tab>
</md-tab-group>
または他のカスタムのもの(jQueryUIスタイル):
<ul>
<li *ngFor="let tab of tabs">{{tab.title}}</li>
</ul>
<div *ngFor="let tab of tabs">
<ng-template [ngTemplateOutlet]="tab.content.templateRef"></ng-template>
</div>
つまり、プロジェクトに最適なコンポーネントを選択したかどうかを心配することなく、タブ(およびその他すべての種類のコントロール)をUIに追加し続けることができます。必要に応じて、後で簡単に切り替えることができます。
(うまくいけば、これはパフォーマンスの問題を引き起こしません!)