我正在使用素材标签:https://material.angular.io/components/tabs/overview
我有一个带有标签视图的页面,我想用它来点击按钮为每个标签填充一个自定义组件。假设我有两个组件,其中包含选择器“customcomponent1”和“customcomponent2”。我想这样做,当我点击一个按钮时,一个新的'对象'将被添加到列表中,如下所示:
{ 'COMPONENT_NAME': 'customcomponent1'}
并且会动态创建一个新的标签“Tab2”,其内部的组件如果是这样的话:
<mat-tab-group>
<mat-tab label="Tab1">
<button (click)="createNewTabWithComponent('customcomponent1')"></button>
</mat-tab>
<mat-tab label="Tab2">
<customcomponent1></customcomponent1>
</mat-tab>
</mat-tab-group>
如果我再次点击该按钮...我会看到一个新标签:
<mat-tab-group>
<mat-tab label="Tab1">
<button (click)="createNewTabWithComponent('customcomponent1')"></button>
</mat-tab>
<mat-tab label="Tab2">
<customcomponent1></customcomponent1>
</mat-tab>
<mat-tab label="Tab3">
<customcomponent2></customcomponent2>
</mat-tab>
</mat-tab-group>
如何使用angular2 / 4实现此目的?我通常会使用类似$ compile的东西,但我不认为angular2 / 4有一个。我想避免的是为每个组件创建一堆标签(想象我有10个组件,真的不想为每个组件创建多个mat-tab占位符,并在每个组件上设置show / hide标志)和'硬编码'内部的一个组件。
如果解决方案显示了如何使用选择器的名称“动态地”添加组件(用户在文本框中输入并点击按钮'',那么我认为这不是一个特定于标签的问题。添加到列表',这也是一个考虑的答案。
我能想到的一个例子是,是否有文本框,有人可以输入任何字符串。如果字符串与组件的名称匹配,则该组件以编程方式“动态显示”在屏幕上,就像它是原始模板的一部分一样。
我所知道的我所知道的伪代码不存在(我想,但如果它确实会很好):
<button (click)="addNameOfComponentToListOfCustomComponentStrings('text-from-a-textbox')">Add Component</button>
<div *ngFor="let string_name_of_component in listofcustomcomponentstrings">
<{{string_name_of_component}}><</{{string_name_of_component}}>
</div>
将它们放入标签会更好。如果这不可行,请发布。否则,请发帖。
如果可能使用某些“嵌套角度路由”可以完成一个解决方法,它会根据ngFor中组件的名称提取组件,这可能也有效......对任何想法都持开放态度。< / p>
答案 0 :(得分:0)
对于你陈述的问题(不希望有10个占位符mat-tabs和* ngIf来隐藏东西),你可以在路由和延迟加载方面做得更好。
说,您的带标签的网页位于/tabs
,其中的模块位于MyTabsModule
。您对该模块的路由配置如下所示:
const routes = [{
path: 'tabs',
component: MyTabsComponent,
}];
我们假设您有两个标签left
和right
。您现在需要的是在惰性模块中添加子组件。简单的事情:
const routes = [{
path: 'tabs',
component: MyTabsComponent,
children: [
{
path: 'left',
loadChildren: 'app/lazy/left.module#LazyLeftModule'
},
{
path: 'right',
loadChildren: 'app/lazy/right.module#LazyRightModule'
}
]
}];
你的MyTabsComponent
需要以某种方式显示,如何?这是Material文档所说的(简化):
<h2>My tabs component</h2>
<nav mat-tab-nav-bar>
<a mat-tab-link routerLink="left">Left tab</a>
<a mat-tab-link routerLink="right">Right tab</a>
</nav>
<router-outlet></router-outlet>
但如果您有多个标签怎么办?好吧,文档说 this ,我简化了前面的例子:
<a mat-tab-link
*ngFor="let link of navLinks"
routerLinkActive #rla="routerLinkActive"
[routerLink]="link.path"
[active]="rla.isActive">
{{link.label}}
</a>
现在,您可以创建多个惰性组件,以及这些延迟路由的路由配置。事实上,您甚至可以创建一个脚本来生成 1 您的路由配置,甚至是那些作为构建的一部分的懒惰模块。
1 - Angular Schematics?
假设您知道自己拥有哪些组件。如果您只是想让用户输入您的文本框,该怎么办?并自己挑选任何组件?比如,有一个下拉列表,并让用户决定他们想要的所有标签,然后在那之上,懒洋洋地加载它们?
那怎么样?首先,你的路线孩子现在有点不同了:
{
path: 'tabs',
component: MyTabsComponent,
children: [{
path: ':tab',
loadChildren: 'app/lazy/lazy.module#LazyWrapperModule',
}
}
现在,您的LazyWrapperModule
导出LazyWrapperComponent
。该组件在模板中具有自己的本地路由器插座。它还基于url加载组件:
constructor(private route: ActivatedRoute, private router: Router) {}
ngOnInit() {
this.activatedRoute.params.subscribe(params => {
const tab = params.tab;
if (!this.isRouteValid(tab)) {
return this.router.navigate(['error'])
}
this.router.navigate([tab]);
});
}
LazyWrapperModule也有路由器配置:
@NgModule({
imports: [
RouterModule.forChild([{
path: 'tab1',
component: Tab1
},
{
path: 'tab2',
component: Tab2
},
...
{
path: 'tab100',
component: Tab100
}]
],
...
现在,这看起来更好。您可以从TabsModule导航到任何内容。然后它通过参数加载组件。你的组件可以,例如如果用户输入了错误的选项卡,则显示错误选项卡,或者您可以简单地为提前输入提供&#34;允许&#34;的列表。标签等等。有趣的东西!
但是,如果您不想限制用户,该怎么办?你想让他们输入&#34;我最动态的组件 - #34;在那个文本框中?
您可以简单地动态创建组件。再次有一个包装器组件,用于创建和注入组件。例如。模板:
<input [(ngModel)]="name" required>
<textrea [(ngModel)]="template">
<button (click)="createTemplate()">Create template</button>
<div #target></div>
然后组件可以是:
class MyTabsComponent {
@ViewChild('target') target;
name = 'TempComponent';
template: '<span class="red">Change me!</span>';
styles: ['.red { border: 1px solid red; }']
constructor(private compiler: Compiler,
private injector: Injector,
private moduleRef: NgModuleRef<any>) {
}
createTemplate() {
const TempComponent = Component({ this.template, this.styles})(class {});
const TempModule = NgModule({
declarations: [TempComponent]
})(class {});
this.compiler.compileModuleAndAllComponentsAsync(TempModule)
.then((factories) => {
const f = factories.componentFactories[0];
const cmpRef = f.create(this.injector, [], null, this.m);
cmpRef.instance.name = this.name;
this.target.insert(cmpRef.hostView);
});
}
... // other stuff
}
现在,如何具体使用它,取决于您的具体需求。例如。对于家庭作业,您可以尝试动态创建上面这样的组件,并将其注入上面的<mat-tab-group>
。或者,动态创建到现有组件的路由作为延迟加载的链接。或者......可能性。我们爱他们。
答案 1 :(得分:0)
此示例使用dynamic-component-loader。
在您的情况下,您需要创建模块选项卡,不要使用<mat-tab-group>
insteand。自<mat-tab>
汇编MatTabsModule
后,您无法将视图嵌入<mat-tab-group>
。
示例此代码无效
<mat-tab-group>
<template>
<!-- dynamic -->
</template>
</mat-tab-group>
您需要的是,为您自己的标签创建新组件,并使用viewContainerRef.createComponent
创建组件。
如果你看到我的例子(dynamic-component-loader),我就把这样的模板放在:
<mat-card>
<mat-card-content>
<h2 class="example-h2">Tabs with text labels</h2>
<mat-tab-group class="demo-tab-group">
<mat-tab label="Tab 1">
hallow
</mat-tab>
</mat-tab-group>
<ng-template live-comp></ng-template>
</mat-card-content>
</mat-card>
我将<ng-template live-comp></ng-template>
置于<mat-card-content>
之下而不是<mat-tab-group>
内,因为<mat-tab>
由MatTabsModule
编译。
答案 2 :(得分:0)
注意这是我如何使用它的一个示例,用于更复杂的应用程序,包含动态数据和多个for循环以及子组件(图层)的多个子项。希望它有所帮助。
我也这样做,但后来使用[data]="data"
设置循环和子组件来设置它。
所以你要做的是设置程序选项卡:
[(selectedIndex)]="selectedTab" (selectedTabChange)="tabChanged($event)"
这将确保如果您的标签更改this.selectedIndex将是您当前的标签。所以你现在需要的是带有循环的mat-tab。在您的模板中:
<mat-tab-group [(selectedIndex)]="selectedTab" (selectedTabChange)="tabChanged($event)">
<div *ngFor="let tab of tabs; let j = index">
<mat-tab label={{tab.name}}>
<div *ngIf="selectedIndex === j"> //To ensure your component is not loaded in the dom when it's not selected.
<customcomponent [data]="data"></customcomponent>
<div>
</mat-tab>
<div>
<mat-tab label="...">
// can put input fields here or even a button. Up to you. selectedTabChange not really have to change then.
</mat-tab>
</mat-tab-group>
现在,如果您点击 ...
,则需要添加额外标签在您的组件中:
private tabChanged(_event: any) {
let index = _event.index;
if(index < this.tabs.length){
this.data = this.tabs[index].data; // set data first before showing the component (*ngIf).
this.selectedIndex = index;
if(index === this.tabs.length){
this.tabs.push({name: newName, data: yourData});
this.selectedIndex = this.tabs.length - 1; // length will be 2 now so new tab will be 1 in this tabs.
}
现在,您可以创建一个包含名称和组件所需数据的新选项卡。现在,您可以在自定义组件上添加 ngOnChanges ,并使用 @Input()数据获取数据:any; 。你在那里做什么取决于你。