Передача данных в динамический загрузчик компонентов с помощью службы в Angular - PullRequest
0 голосов
/ 23 апреля 2019

У меня динамический загрузчик компонентов, и мне нужно передать данные через службу.Я могу получить данные для отображения, если я, например, запустил функцию на 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 голосов
/ 26 апреля 2019

После запуска документации Angular я обнаружил, что мне нужно передать данные в экземпляр 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() {
  }

}
...