Rx js Как ожидать завершения всех наблюдаемых от компонентов Dynami c в интеллектуальном компоненте - PullRequest
5 голосов
/ 04 мая 2020

Hellow,

В моем проекте я хочу сделать экспорт (PDF), нажав на кнопку на странице. На моей странице есть панель инструментов, которая динамически создает несколько графиков. Когда я нажимаю кнопку «экспорт», я хочу проинформировать каждый динамический компонент c о создании SVG из текущего экземпляра диаграммы и передать его компоненту «страница». Чтобы иметь возможность создать SVG, я должен подождать, пока не будет создан экземпляр диаграммы. Когда у меня есть все svg из разных динамических c компонентов, я могу передать информацию (разные svg) в тело вызова API, который создаст PDF.

Так что я попытался добиться этого в тестовом проекте . Я не знаю правильного подхода, поэтому я попытался заполнить массив из службы наблюдаемыми в динамических c компонентах.

Проблема, с которой я столкнулся, заключается в том, как я узнаю, что все наблюдаемые (которые возвращают SVG или ошибки при сбое) из динамических c компонентов добавляются в массив перед выполнением forkjoin в посадочная страница компонент. Мой forkJoin также не возвращает svg, поэтому я что-то не так делаю. Но, возможно, есть лучший способ подождать всех динамических c компонентов, пока они не создадут свои svg.

Ответы [ 2 ]

2 голосов
/ 04 мая 2020

Я думаю, что проблема в том, что когда chartInstanceStream$ излучает, у него нет подписчиков, что означает, что он не может завершиться из-за first(), что также означает, что forkJoin не будет излучать, пока не будут отправлены все предоставленные наблюдаемые по крайней мере, один раз и завершено.

chartInstanceStream$ испускается из-за setChartInstance, что происходит до того, как testService.clickExportPdfStream испускает.

Одним быстрым решением было бы превратить chartInstanceStream в ReplaySubject чтобы он воспроизвел свои последние значения для поздних подписчиков .

Итак, с этого:

chartInstanceStream$ = new Subject<Highcharts.Chart>();

до this:

chartInstanceStream$ = new ReplaySubject<Highcharts.Chart>(1);

forkJoin внутренне подписывается на все предоставляемые наблюдаемые. Преобразуя chartInstanceStream$ в ReplaySubject, вы гарантируете, что поздние подписчики (из forkJoin) получат самое старое значение, испускаемое ReplaySubject.

И поскольку ваши наблюдаемые, которые forkJoin подписывается, чтобы выглядеть следующим образом:

this.chartInstanceStream$.pipe(
    map((chart: Highcharts.Chart) =>
      chart.getSVG()
    ),
    first() // !
  )

после того, как они передают одно значение, они также будут немедленно завершены, из-за first().

0 голосов
/ 04 мая 2020

Один из подходов к использованию службы - это один подход.

Другим решением для этого может быть экспорт функции public из компонента dynamici c, а затем вызов их из родительского компонента.

Например, у меня есть простое ChartComponent, которое имитирует экспорт SVG:

@Component({
  selector: "app-chart",
  templateUrl: "./chart.component.html",
  styleUrls: ["./chart.component.css"]
})
export class ChartComponent {
  exportSvg(): Observable<string> {
    return defer(() => 
      timer(Math.random() * 2000 + 500).pipe(mapTo('svg'))
    )
  }
}

Теперь в моем родительском компоненте я запрашиваю эти компоненты и вызываю exportSvg по требованию:

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  @ViewChildren(ChartComponent) charts: QueryList<ChartComponent>;

  onExportClick(): void {
    const charts = this.charts.map(component => component.exportSvg());

    forkJoin(charts).subscribe(console.log)
  }
}

В моем примере я не создавал ChartComponent динамически, но он должен вести себя так же.

Полный пример можно найти в this stackblitz

...