Angular 5-使用ng-content

时间:2018-12-05 16:58:05

标签: angular

我正在开发一个具有多个“应用程序”的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模板”,在该模板中将呈现视图。这是我首先提到的每个具有内容窗格和页脚栏的视图。参见下面的简单示例。

Example of content pane and footer bar

此外,我还有一些功能可以检查#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-contentngTemplateOutlet都不会会正确解析。


To give hands-on about my situation I made the example in stackblitz.

0 个答案:

没有答案