Как добавить новые вкладки с компонентами, используя "имя/селектор компонента" программно, используя angular 2/4?
Я использую вкладки материалов:https://material.angular.io/components/tabs/overview
у меня есть страница с видом вкладки, которую я хочу использовать для заполнения одного пользовательского компонента на вкладку одним нажатием кнопки. Предположим, у меня есть два компонента с селекторами "customcomponent1" и "customcomponent2". Я хочу сделать так, чтобы при нажатии кнопки новый "объект" был добавлен в список следующим образом:
{'имя_компонента':'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 на каждом из них) и "hardcoding" один компонент внутри него.
это на самом деле не проблема, я думаю, что это специфично для вкладок, если решение показывает, как добавить компонент "динамически", используя имя селектора (что пользователь вводит в текстовое поле и нажимает кнопку "Добавить в список", это тоже будет рассмотренный ответ.
образец я могу подумайте, есть ли текстовое поле, и кто-то может ввести любую строку. Если строка соответствует имени компонента, то этот компонент программно "динамически отображается" на экране, как если бы он был частью исходного шаблона.
псевдокод того, что я ищу, что я знаю, не существует (я думаю, но было бы неплохо, если бы это было):
<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 ответов
для вашей заявленной проблемы (не желая иметь 10 заполнителей-вкладок с *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
нужно как-то это показать, как? Вот что!--26-->материал документы говорят (упрощенно):
<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 ваша конфигурация маршрута и даже эти ленивые модули для вас как часть сборки.
1 - Угловые Схемы?
это при условии, что вы знаете, какие компоненты у вас есть. Что делать, если вы просто хотите, чтобы пользователь ввел в это поле твой? И выбрать любой компонент самостоятельно? Например, есть раскрывающийся список, и пусть пользователь решает все вкладки, которые они хотят, а затем, поверх этого, загрузите их лениво?
поле навигации: дети параметризация и компонент оболочки.
как это? Во-первых, ваши дети маршрута теперь немного другие:
{
path: 'tabs',
component: MyTabsComponent,
children: [{
path: ':tab',
loadChildren: 'app/lazy/lazy.module#LazyWrapperModule',
}
}
сейчас, ваш LazyWrapperModule
экспорт a 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 перейти к чему угодно. Затем он загружает компонент по параметру. Ваш компонент может, например, показать вкладку ошибки, если пользователь вводит неправильную вкладку,или вы можете просто предоставить typeahead со списком" разрешенных " вкладок и т. д. Забавная штука!
но что, если вы не хотите ограничивать пользователя? Вы хотите, чтобы они набрали "мой самый динамичный компонент" в этом текстовом поле?
Dynamic all: динамическое создание компонентов
вы можете просто создайте компонент динамически. Снова есть компонент-оболочка, который создает и вводит компонент. Е. Г. шаблон:
<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>
еще выше. Или динамически создайте маршрут к существующему компоненту в виде лениво загруженной ссылки. Или... размеренности. Мы их любим.
этот образец для использования динамический компонент-погрузчик.
в вашем случае, вам нужно создать вкладку модуль, не использовать <mat-tab-group>
insteand. С <mat-tab>
составленному MatTabsModule
, вы не можете встроить представление в <mat-tab-group>
.
пример этот код не будет работать
<mat-tab-group>
<template>
<!-- dynamic -->
</template>
</mat-tab-group>
вам нужно создать новый компонент для вашей собственной вкладки и использовать viewContainerRef.createComponent
создать компонент.
если вы видите мой пример (динамический компонент-погрузчик), я ставлю шаблон так:
<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
.
Примечание это пример того, как я использую это, для более сложного приложения с динамическими данными и несколькими циклами и несколькими чайлдами компонентов Чайлдса (слой). Надеюсь, это поможет.
Я делаю это также, но затем с циклом и дочерним компонентом, используя [data]="data"
, чтобы установить его.
Итак, что вы хотите сделать, это установить вкладку programmatic с:
[(selectedIndex)]="selectedTab" (selectedTabChange)="tabChanged($event)"
это гарантирует, что если ваша вкладка изменит это.свойства 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 на customcomponent и получить данные с @Input () data: any;. Какие у вас вообще есть до вы.