更新:
正确的问题可能是:"如何实现我自己的ng-container
版本,以便我可以使用&#34; <my-component ...>...</my-component>
&#34;而不是&#34; <ng-container my-directive ...>...</ng-container>
&#34;。
我正在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>
然后,如果我需要更改标签的工作方式,它会在一个地方发生。但是,如果我们使用包装器组件,那么HTML会受到与所需标签交错的主机元素标签的污染,这显然会使事情变得混乱,例如。
<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组件中打开/删除主机元素的原因 - 这会让这很简单。
通常的答案是使用属性选择器,例如:
Remove the angular2 component's selector tag
How to remove/replace the angular2 component's selector tag from HTML
但是,这意味着我们需要知道使用网站上的标签结构,这显然会破坏使用精美命名的包装标签的全部意义。
因此,最后,我该怎么做呢?由于性能原因,它可能不可能吗?我已经开始关注Renderer2但是没有找到一种明显的方法来做我想做的事情,我想避免非角度的DOM黑客攻击。
答案 0 :(得分:4)
我有一个有效的解决方案,虽然不像我想的那样优雅。我更倾向于选择“摆脱宿主元素”,因为这样会更加整洁。
我通过简单地检查他们如何在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>
这是我的选项卡包装器组件的最小实现,其中包括单个选项卡和选项卡内容的两个指令。
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模板来呈现不同类型的选项卡。
e.g。对于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>
e.g。对于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>
这意味着我们现在可以在整个地方继续向用户界面添加标签(以及所有其他类型的控件),而无需担心我们是否为项目选择了最佳组件 - 如果需要,我们可以在以后轻松切换
(希望这不会引入性能问题!)