Как избежать переключения ng и генерировать компонент по типу
В настоящее время я получаю массив (в коде ниже resourceTypes) и с ngSwitch. Как вы можете видеть в зависимости от TypeName, я создаю различные виды компонентов / директив (config-resource-editor или mvc-resource-editor или...). Однако мне не нравится этот код, потому что когда я создаю больше редакторов ресурсов, мне всегда приходится модифицировать этот код и т. Д. Как я могу реорганизовать код, чтобы я мог просто создать правильный редактор ресурсов в зависимости от типа без ng-switch. Я посмотрел на ng-content, думаю, что с этим нужно что-то делать, но я не вижу этого.
TL; DR: Как я могу изменить код, приведенный ниже, чтобы мне больше не приходилось использовать ngSwitch, а "связывал" тип с компонентом.
<div *ngFor="let aType of resourceTypes; let i = index" role="tabpanel" class="tab-pane" [ngClass]="{'active': i==0}" [attr.id]="aType.Name">
<span *ngSwitch="aType.Name">
{{aType.Name}} tab content here
<config-resource-editor [application]="application" ngSwitchWhen="Config" template></config-resource-editor>
<mvc-resource-editor [application]="application" ngSwitchWhen="MVC" template></mvc-resource-editor>
<other-resource-editor [application]="application" ngSwitchWhen="Other" template></other-resource-editor>
<wcf-resource-editor [application]="application" ngSwitchWhen="WCF" template></wcf-resource-editor>
<web-resource-editor [application]="application" ngSwitchWhen="Web" template></web-resource-editor>
<webapi-resource-editor [application]="application" ngSwitchWhen="WebAPI" template></webapi-resource-editor>
</span>
</div>
Если что-то не понятно, не стесняйтесь спрашивать.
заранее спасибо
1 ответ
Вы можете создать компонент-оболочку и использовать @ViewChild, чтобы избавиться от случая переключения.
Ваша обертка будет выглядеть примерно так:
@Component({
selector: 'my-wrapper',
template: `<div #target></div>`
})
export class MyWrapper {
@ViewChild('target', {read: ViewContainerRef}) target;
cmpRef: ComponentRef<Component>;
currentComponent: Component;
private isViewInitialized: boolean = false;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private cdRef: ChangeDetectorRef,
private wrapperService: WrapperService
){}
updateComponent() {
if (!this.currentComponent) {
return;
}
if (!this.isViewInitialized) {
return;
}
if (this.cmpRef) {
this.cmpRef.destroy();
}
let factory = this.componentFactoryResolver.resolveComponentFactory(this.currentComponent);
this.cmpRef = this.target.createComponent(factory);
}
ngAfterViewInit() {
this.cdRef.detectChanges();
this.isViewInitialized = true;
this.currentComponentSubscription = this.wrapperService.getCurrentComponent().subscribe(c => {
this.currentComponent = c;
if (c) {
this.updateComponent();
}
});
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
if(this.currentComponentSubscription){
this.currentComponentSubscription.unsubscribe()
}
}
}
Создайте WrapperService и напишите метод получения / установки для текущего компонента и обратите внимание, что метод получения должен возвращать объект BehaviorSubject:
private _currentComponent: BehaviorSubject<Component> = new BehaviorSubject(null);
getCurrentComponent(): BehaviorSubject<Component> {
return this._currentComponent;
}
setCurrentComponent(value: Component) {
this._currentComponent.next(value);
}
Используйте селектор для компонента MyWrapper вместо вашего ngSwitch и установите текущий компонент, используя службу оболочки в родительском компоненте, где определен resourceTypes.
Вам также необходимо добавить компоненты, присоединяемые / отключаемые к entryComponents в @NgModules:
@NgModule({
entryComponents: <array of components>
})
Примечание. При настройке _currentComponent необходимо указать ссылку на компонент, а не строку.
Кредиты: @Gunter Я также сослался на его пример, чтобы это пошло для меня.