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

时间:2019-04-23 19:57:44

标签: javascript angular viewchild

我有一个动态组件加载程序,并且我需要通过服务传递数据。例如,如果我在click上触发函数,但在OnInit上触发函数,则可以显示数据。

  • 我尝试使用AfterViewInit
  • 最终数据将来自API

更新: 工作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-details.component.ts

export class AnswerSetHeaderDetailsComponent implements OnInit {

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

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

}

1 个答案:

答案 0 :(得分:0)

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

Future<FooState> bar(int event)

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

最终,我希望传递多个组件,但现在可以使用。

header.component.html

int

header.component.ts

Event

header.service.ts

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

header.ts

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

main.component.html

/* ... */

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();
  }

}

main.component.ts

/* ... */

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

  constructor() { }

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

main.module.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-header-details.component.html

<app-header></app-header>

main-header-details.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);
  }

}