首页 文章

如何使用角度2/4以编程方式使用'name/selector of the component'添加包含组件的新选项卡?

提问于
浏览
7

我正在使用材料标签: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中组件的名称提取组件,这可能也有效......对任何想法都是开放的 .

3 回答

  • 0

    对于你陈述的问题(不希望有10个占位符mat-tabs和* ngIf来隐藏东西),你可以通过路由和延迟加载做得更好 .

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

    比如说,带有标签的页面位于 /tabs ,带有它的模块是 MyTabsModule . 您对该模块的路由配置如下所示:

    const routes = [{
      path: 'tabs',
      component: MyTabsComponent,
    }];
    

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

    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 docs所说的(简化):

    <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>
    

    但如果您有多个标签怎么办?好吧,文档说这个,我简化了前面的例子:

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

    所以现在,您可以创建几个惰性组件,以及这些延迟路由的路由配置 . 实际上,你甚至可以创建一个脚本来生成你的路由配置,甚至是那些作为构建的一部分的懒人模块 .

    1 - Angular Schematics

    假设您知道自己拥有哪些组件 . 如果您只是想让用户输入您的文本框,该怎么办?并自己挑选任何组件?比如,有一个下拉列表,并让用户决定他们想要的所有选项卡,然后在其上,懒洋洋地加载它们?

    文本框导航:参数化子项和包装器组件 .

    怎么样?首先,您的路线孩子现在有点不同:

    {
      path: 'tabs',
      component: MyTabsComponent,
      children: [{
        path: ':tab',
        loadChildren: 'app/lazy/lazy.module#LazyWrapperModule',
      }
    }
    

    现在,您的 LazyWrapperModule 导出 LazyWrapperComponent . 该组件在模板中具有自己的本地路由器插座 . 它还会根据网址加载组件:

    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导航到任何内容 . 然后它通过参数加载组件 . 你的组件可以,例如如果用户输入了错误的选项卡,则显示错误选项卡,或者您只需提供带有“允许”选项卡列表的预先输入等等 . 有趣的东西!

    但是,如果您不想限制用户呢?你想让他们在那个文本框中输入“my-most-dynamic-component-ever”?

    动态全部:动态创建组件

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

    <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> 以上 . 或者,动态创建到现有组件的路由作为延迟加载的链接 . 或者......可能性 . 我们爱他们 .

  • 0

    此示例使用dynamic-component-loader .

    在您的情况下,您需要创建模块选项卡,不要使用 <mat-tab-group> insteand . 自 MatTabsModule 编译 <mat-tab> 以来,您无法将视图嵌入 <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 编译 .

  • 0

    Note 这是我如何使用它的一个例子,用于更复杂的应用程序,包括动态数据和多个for循环以及childs组件(layer)的多个子项 . 希望能帮助到你 .

    我这样做,但随后使用 [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.
        }
    

    现在,您可以创建一个新选项卡,其中包含名称和组件所需的数据 . 现在,您可以在customcomponent上添加 ngOnChanges 并使用 @Input() data: any; 获取数据 . 你在那里做什么取决于你 .

相关问题