关于使用TypeScript的Angular。
我来自WPF世界,现在我正在尝试一些Angular开发。
我的应用程序中有一个地方,我想要在其中建立一个选项卡控件以包含各种“打开”文档(Angular组件)。也许我认为这是完全错误的,但让我们考虑一下,其中有一个包含TabItem
的数组的可注射对象,其中TabItem
的属性之一是类的字符串,工厂或类型的名称我的应用程序的组件(待确定)。
export class TabItem {
public title : string;
public disabled : boolean;
public active : boolean;
public factory: any; // or class name or object
}
@Injectable()
export class OpenDocumentService {
openTabs: Array<TabItem> = [];
addTab(t:TabItem){ openTabs.push(t); }
}
在WPF中,我将创建一个内容演示者,并将其绑定到要显示的名称或对象。
我将在Angular世界中做什么。备注:要显示的组件可能在其他模块中。
添加到服务中后,如何* ngFor它并显示任意组件? (替换ng-contentpresenter)
<tabset>
<tab *ngFor="let tabz of tabservice.openTabs"
[heading]="tabz.titel">
<ng-contentpresenter use={{tabz?.factory}}/>
</tab>
</tabset>
答案 0 :(得分:1)
对于任何在这里结束的人:
简短的回答-角度不赞成此类的Shinnigan,因此,对于具有项浏览器的复杂情况,您最好坚持建议的构造ui的方法-如templates injection,正确使用路由ngSwitch树添加@ ngrx / store等。
长答案-look here。 您必须首先建立基础设施:
import { Injectable, Type } from '@angular/core';
/**
* This service allows dynamically bind viewModel and component in configuration stage and then resolve it in render stage.
* Service for dynamic component registration and detection. Component to be used are determined based on view model they have to render.
*/
@Injectable()
export class DataTemplateService {
private dictionary: Map<Type<any>, Type<any>>;
constructor() {
this.dictionary = new Map<Type<any>, Type<any>>();
}
/**
* Determines component class, searching in registered components.
* @param data ViewModel that will be used for component, returns undefined if not found.
*/
public determine(data: any): Type<any> | undefined {
return data ? this.dictionary.get(data.constructor) : undefined;
}
/**
* Registers binding of certain view model towards certain component.
* @param viewModelType Type of ViewModel to be registered.
* @param componentType Type of Component to be registered.
*/
public register(viewModelType: Type<any>, componentType: Type<any>) {
this.dictionary.set(viewModelType, componentType);
}
}
import { ComponentFactoryResolver, Injectable } from '@angular/core';
/**
* Service fro rendering dynamic components.
*/
@Injectable()
export class ComponentRendererService {
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private dataTemplateService: DataTemplateService) {
}
public render(data: any, containerComponent: any) {
setTimeout(() => {
this.doRender(data, containerComponent);
}, 0);
}
/**
* Renders dynamic components based on ViewModel they have to use.
* @param data Collection of ViewModels that have to be used to render all child components.
* @param containerComponent Parent component, that have to host dynamic child components.
*/
public renderAll(data: Array<any>, containerComponent: any) {
setTimeout(() => {
if (data) {
data.forEach(item => {
this.doRender(item, containerComponent);
});
}
}, 0);
}
private doRender(data: any, containerComponent: any) {
if (!data) {
console.debug('No data (viewModel) for ComponentRendererService to render.');
return;
}
const viewContainerRef = containerComponent.viewContainerRef;
const dataItem = data;
const component = this.dataTemplateService.determine(dataItem);
if (component) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
const componentRef = viewContainerRef.createComponent(componentFactory);
(<any>componentRef.instance).data = data;
} else {
console.warn('Failed to find component for viewmodel of type' + dataItem.constructor);
}
}
}
import { Directive, ViewContainerRef, Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
/**
* Directive that enables construction of dynamic child components.
*/
@Directive({
selector: '[dynamic-component-host]',
})
export class DynamicComponentHostDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
/**
* Component that represents extention point for rendering dynamic child components.
*/
@Component({
selector: 'ext-point-single-host',
template: `
<div class="ext-point-host">
<ng-template dynamic-component-host></ng-template>
</div>
`
})
export class ExtPointSingleHostComponent implements OnChanges {
@ViewChild(DynamicComponentHostDirective) public hostDirective: DynamicComponentHostDirective;
@Input() public viewModel: any;
constructor(private componentRenderer: ComponentRendererService) { }
/**
* Loads nested components.
*/
public loadComponent() {
const viewModel = this.viewModel;
this.componentRenderer.render(viewModel, this.hostDirective);
}
public ngOnChanges(changes: SimpleChanges) {
this.hostDirective.viewContainerRef.clear();
this.loadComponent();
}
}
之后,您可以将模型绑定到模块中的组件:
@Component({
template: '<button type="button" class="">Custom style 2</button>'
})
export class CustomButton1Component {
public data: CustomButton1ViewModel;
}
export class CustomButton1ViewModel {
}
@Component({
template: '<button type="button" class="">Custom style 2</button>'
})
export class CustomButton2Component {
public data: CustomButton2ViewModel;
}
export class CustomButton2ViewModel {
}
@NgModule({
...
providers: [..., DataTemplateService]
})
export class DemoModule {
constructor(dataTemplateService: DataTemplateService) {
dataTemplateService.register(CustomButton2ViewModel, CustomButton2Component);
dataTemplateService.register(CustomButton1ViewModel, CustomButton1Component);
}
}
就是这样!
现在,我们可以使用其viewModel属性并将其绑定到CustomButton1ViewModel或CustomButton2ViewModel,以实际呈现CustomButton2Component或CustomButton1Component。为什么需要那么多代码?好吧,寻找简短答案:(