使用 Angular 中的服务将数据传递给动态组件加载器

Passing data to a dynamic component loader using a service in Angular

我有一个动态组件加载器,我需要通过服务传入数据。例如,如果我在 click 上触发该函数,但不是 OnInit.

,我可以获得要显示的数据

更新: 工作 StackBlitz

app.component.html

<app-answer-set></app-answer-set>

header.component.html

<ng-template appHeaderHost></ng-template>

header.component.ts

export class HeaderComponent implements OnInit {
  @Input() component;
  @ViewChild(HeaderHostDirective) headerHost: HeaderHostDirective;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

  ngOnInit() {
    this.loadComponent();
  }

  loadComponent() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.component);
    const viewContainerRef = this.headerHost.viewContainerRef;

    viewContainerRef.createComponent(componentFactory);
  }

}

header-host.directive.ts

export class HeaderHostDirective {

  constructor(public viewContainerRef: ViewContainerRef) { }

}

header-data.service.ts

export class HeaderDataService {

  private headerDataSource = new Subject<any>();
  headerData$ = this.headerDataSource.asObservable();

  constructor() { }

  setHeaderData(data: any) {
    this.headerDataSource.next(data);
  }

}

answer-set.component.html

<app-header [component]="component"></app-header>
<button (click)="setHeaderData()">click</button>

answer-set.component.ts

export class AnswerSetComponent implements OnInit {
  component: any = AnswerSetHeaderDetailsComponent;

  constructor(private headerDataService: HeaderDataService) { }

  ngOnInit() {
    this.setHeaderData();
  }

  setHeaderData() {
    const data = [{name: 'Header stuff'}];
    this.headerDataService.setHeaderData(data);
  }

}

answer-set-header-details.html

<dt>First:</dt>
<dd>Description</dd>
<dt>Second</dt>
<dd>Description</dd>
<p>
  data will show on click of button but not <code>onInit</code>:
</p>
<p>
  {{headerData}}
</p>

answer-set-header-详情.component.ts

export class AnswerSetHeaderDetailsComponent implements OnInit {

  constructor(private headerDataService: HeaderDataService) { }
  headerData: any;

  ngOnInit() {
    this.headerDataService.headerData$
      .subscribe(data => {
        this.headerData = data[0].name;
      });
  }

}

在 运行 通过 Angular documentation 之后,我发现我需要将数据传递给 componentRef 实例,如下所示:

(<IHeader>componentRef.instance).data = headerItems.data;

经过一些重构后,我最终通过服务传递了组件和数据,请参阅更新后的 StackBlitz 以获取工作示例。

最终我会考虑传递多个组件,但目前这可行。

header.component.html

<ng-template appHeaderHost></ng-template>

header.component.ts

/* ... */

export class HeaderComponent implements OnInit {
  @ViewChild(HeaderHostDirective) headerHost: HeaderHostDirective;

  subscriptionManager = new Subscription();

  constructor(
    private headerService: HeaderService,
    private componentFactoryResolver: ComponentFactoryResolver
  ) {

    const headerConfigSubscription = this.headerService.headerConfig$
      .subscribe((headerItems: HeaderItem) => {
        this.loadComponent(headerItems);
      });

    this.subscriptionManager
      .add(headerConfigSubscription);
  }

  ngOnInit() {
  }

  loadComponent(headerItems: HeaderItem) {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(headerItems.component);
    const viewContainerRef = this.headerHost.viewContainerRef;

    viewContainerRef.clear();

    const componentRef = viewContainerRef.createComponent(componentFactory);

    (<IHeader>componentRef.instance).data = headerItems.data ? headerItems.data : null;
  }

  ngOnDestroy() {
    /**
     * @description Unsubscribe from all subscriptions
     * to prevent memory leaks
     */
    this.subscriptionManager.unsubscribe();
  }

}

header.service.ts

/* ... */

export class HeaderService {
  private headerConfigSource = new Subject<any>();
  headerConfig$ = this.headerConfigSource.asObservable();

  constructor() { }

  configureHeaderItems(headerItems: HeaderItem) {
    this.headerConfigSource.next(headerItems);
  }
}

header.ts

/* ... */

export interface IHeader {
  data: any;
}

export interface IHeaderItem {
  component: Type<any>;
  data?: any;
}

export class HeaderItem implements IHeaderItem {
  constructor(
    public component: Type<any>,
    public data?: any
  ) {
    this.component = component;
    this.data = data;
  }
}

main.component.html

<app-header></app-header>

main.component.ts

export class MainComponent implements OnInit {
  headerItems: HeaderItem;

  constructor(private headerService: HeaderService) { }

  ngOnInit() {
    this.configureHeaderItems();
  }

  configureHeaderItems() {
    this.headerItems = new HeaderItem(MainHeaderDetailsComponent, {});

    this.getHeaderItemData();
  }

  /**
   * @description This is where we would make the API call and get the data
   * but we can mock it for now 
   */
  getHeaderItemData() {
    const data = {
      name: 'I am loaded dynamically!'
    };

    this.headerItems.data = data;
    this.headerService.configureHeaderItems(this.headerItems);
  }

}

main.module.ts

@NgModule({
  /* ... */

  // Need to add to entryComponents
  entryComponents: [MainHeaderDetailsComponent]
})
export class AnswerSetModule { }

main-header-details.component.html

<p>
  Header content. {{data.name}}
</p>

main-header-details.component.ts

/* ... */

export class MainHeaderDetailsComponent implements OnInit {

  constructor() { }
  @Input() data: any;

  ngOnInit() {
  }

}