如何避免ng切换并在类型上生成组件

时间:2016-12-30 13:12:15

标签: javascript angular typescript code-design

我目前正在获取一个数组(在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 个答案:

答案 0 :(得分:2)

您可以创建一个包装器组件并使用@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并为当前组件编写一个getter / setter,并注意getter应该返回一个BehaviorSubject:

private _currentComponent: BehaviorSubject<Component> = new BehaviorSubject(null);
getCurrentComponent(): BehaviorSubject<Component> {
    return this._currentComponent;
}

setCurrentComponent(value: Component) {
   this._currentComponent.next(value);
}

使用MyWrapper组件的选择器代替ngSwitch,并使用父组件中定义resourceTypes的包装器服务设置当前组件。

您还需要将附加/分离的组件添加到@NgModules中的entryComponents:

@NgModule({
    entryComponents: <array of components>
})

注意:在设置_currentComponent时,您需要提供对组件的引用而不是字符串。

致谢:@Gunter我也提到了他的例子来帮助我。