如何使用角度2/4以编程方式使用"组件的名称/选择器"添加包含组件的新选项卡?

Rol*_*ndo 7 material angular

我正在使用材料标签: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>
Run Code Online (Sandbox Code Playgroud)

如果我再次点击该按钮...我会得到一个新标签:

<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>
Run Code Online (Sandbox Code Playgroud)

如何使用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>
Run Code Online (Sandbox Code Playgroud)

将它们放入选项卡将是一个加号.如果这不可行,请发布.否则,请发布.

如果可能使用某些"嵌套角度路由"可以完成一个解决方法,它会根据ngFor中组件的名称提取组件,这可能也有效......对任何想法都是开放的.

Zla*_*tko 1

对于您提出的问题(不希望有 10 个带有 *ngIf 的占位符 mat-tabs 来隐藏内容),您可以通过路由和延迟加载做得更好。

简单的导航:修复了延迟加载的路径。

假设您的带有选项卡的页面位于/tabs,带有它的模块位于MyTabsModule。该模块的路由配置如下所示:

const routes = [{
  path: 'tabs',
  component: MyTabsComponent,
}];
Run Code Online (Sandbox Code Playgroud)

假设您有两个选项卡,leftright。您现在需要的是在惰性模块中添加子组件。就像这样简单的事情:

const routes = [{
  path: 'tabs',
  component: MyTabsComponent,
  children: [
    {
      path: 'left',
      loadChildren: 'app/lazy/left.module#LazyLeftModule'
    },
    {
      path: 'right',
      loadChildren: 'app/lazy/right.module#LazyRightModule'
    }
  ]
}];
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

但是如果您有多个选项卡怎么办?好吧,文档是这样说的,我简化了前面的示例:

<a mat-tab-link
  *ngFor="let link of navLinks"
  routerLinkActive #rla="routerLinkActive"
  [routerLink]="link.path"
  [active]="rla.isActive">
    {{link.label}}
</a>
Run Code Online (Sandbox Code Playgroud)

所以现在,您可以创建几个惰性组件,以及这些惰性路由的路由配置。事实上,您甚至可以创建一个脚本来生成1您的路由配置,甚至作为构建的一部分为您生成那些惰性模块。

1 -角度示意图

这是假设您知道自己拥有哪些组件。如果您只想让用户在您的文本框中输入内容怎么办?并自己选择任何组件?就像,有一个下拉菜单,让用户决定他们想要的所有选项卡,然后最重要的是,延迟加载它们?

文本框导航:参数化子组件和包装组件。

进展如何?首先,你的路线孩子现在有点不同了:

{
  path: 'tabs',
  component: MyTabsComponent,
  children: [{
    path: ':tab',
    loadChildren: 'app/lazy/lazy.module#LazyWrapperModule',
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,您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]);
  });
}
Run Code Online (Sandbox Code Playgroud)

LazyWrapperModule 也有路由器配置:

@NgModule({
  imports: [
    RouterModule.forChild([{
      path: 'tab1',
      component: Tab1
    },
    {
      path: 'tab2',
      component: Tab2
    },
    ...
    {
      path: 'tab100',
      component: Tab100
    }]
  ],
  ...
Run Code Online (Sandbox Code Playgroud)

现在,这看起来好多了。您可以从 TabsModule 导航到任何内容。然后它通过参数加载一个组件。例如,如果用户输入错误的选项卡,您的组件可以显示错误选项卡,或者您可以简单地提供预先输入“允许的”选项卡列表等。有趣的东西!

但是如果您不想限制用户怎么办?您想让他们在该文本框中输入“我的最动态组件”吗?

Dynamic all:动态创建组件

您可以简单地动态创建一个组件。再次有一个包装器组件来创建和注入组件。例如模板:

<input [(ngModel)]="name" required>
<textrea [(ngModel)]="template">
<button (click)="createTemplate()">Create template</button>

<div #target></div>
Run Code Online (Sandbox Code Playgroud)

那么该组件可以是这样的:

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
}
Run Code Online (Sandbox Code Playgroud)

现在,具体如何使用它,取决于您的具体需求。例如,对于家庭作业,您可以尝试动态创建像上面这样的组件,并将其注入到<mat-tab-group>上面的更多内容中。或者,动态创建到现有组件的路由作为延迟加载的链接。或者……可能性。我们爱他们。