我正在开发一个具有多个“应用程序”的Web应用程序。 每个应用包含一个包含1个或多个视图的工作流。一次仅显示一个视图,每个视图仅有的两个共同之处是它们具有内容窗格和页脚栏,并且所有视图共享相同的数据工作集< / strong>。其背后的想法是,有可能在视图运行时进行“特定于客户的”替代,或向工作流中添加其他自定义视图。
对于这些应用程序,我创建了一个BaseComponent
来处理初始加载,视图转换和我在示例中遗漏的其他内容。
这些应用程序本身就是组件,它们在此BaseComponent
上进行扩展,并在super()
方法中传递视图列表。
自定义应用组件-我们的自定义应用,我们将加载其中的
// Example list of views. Each view is an object containing an id and a component.
const views = [view1, view2, view3, ...];
@Component({
selector: 'custom-app',
templateUrl: '../base/base.component.html'
})
export class CustomAppComponent extends BaseComponent {
constructor() {
super(views); // Load in the views into the BaseComponent
}
}
基本组件-每个应用程序的基本功能都应该处理所有魔术
@Component({
templateUrl: './base.component.html'
})
export class BaseComponent implements OnInit {
private componentFactoryResolver: ComponentFactoryResolver;
private views: AppView[];
workset: AppWorkset;
@ViewChild(ViewHostDirective)
private viewHost: ViewHostDirective;
constructor(views: AppView[] = []) {
// Custom injector to be able to inject directly inject them
// without having to pass on in the super method
const injector = AppInjector.get();
this.componentFactoryResolver = injector.get<ComponentFactoryResolver>(<any>ComponentFactoryResolver);
this.workset = new AppWorkset();
this.workset.nextView = (id) => this.nextView(id);
this.views = views;
}
ngOnInit() {
// Load the first view
this.nextView(Views.First);
}
nextView(id: number) {
const view = this.views.find(v => v.id === id);
if (view == null) {
console.log('No view with id', id);
return;
}
// Clear the current view
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(view.component);
const viewContainerRef = this.viewHost.viewContainerRef;
viewContainerRef.clear();
// Update the viewHost with the new component that will serve as our new view
const componentRef = viewContainerRef.createComponent(componentFactory);
// Pass on our shared workset
(<AppViewComponent>componentRef.instance).workset = this.workset;
}
}
View Host指令-能够在运行时使用新组件更改视图的指令
@Directive({
selector: '[view-host]',
})
export class ViewHostDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
基本组件的html
<h3>My app</h3>
<ng-template view-host></ng-template>
视图示例-所有视图将具有自己的自定义逻辑,它们仅共享工作集
@Component({
template: `
<h4>First view</h4>
<button (click)="next()">Next view</button>
`
})
class FirstView implements AppViewComponent {
@Input() workset: AppWorkset;
next() {
this.workset.nextView(Views.Second);
}
}
现在,这一切都很好。我要实现的最困难的部分是要有一个默认的“ html模板”,在该模板中将呈现视图。这是我首先提到的每个具有内容窗格和页脚栏的视图。参见下面的简单示例。
此外,我还有一些功能可以检查#appcontent
内容的更改,因此它可以应用一些正确的大小(滚动条等)。具有“魔术”基础组件的想法是使应用及其视图本身尽可能简单,因此,该“ html模板”也应成为基础的一部分。通常,要实现此目的,您可以使用ng-content将孩子的html添加到父级的html时,将其拆分到正确的位置。
<div class="my-content-css" #appcontent>
<ng-content select=".app-content"></ng-content>
</div>
<div class="my-footer-css" #appfooter>
<ng-content select=".app-footer"></ng-content>
</div>
但是在这种情况下,我无法使用它,因为<ng-template view-host></ng-template>
会将视图的内容放在ng-component
元素中,因此ng-content
和ngTemplateOutlet
都不会会正确解析。
To give hands-on about my situation I made the example in stackblitz.