Понимание RxJS BehaviorSubject

Мы пытаемся использовать BehaviorSubject для обмена данными API между несколькими компонентами. В моем компоненте я запускаю HTTP-запрос и обновляю тему после получения ответа.

component.ts

onClick() {
    this.service.getCompanies();
    this.service.companiesList$.subscribe(companies => {
        console.log(companies.length); // 0 and then 1
    });
}

service.ts

companiesList$ = new BehaviorSubject([]);

getCompanies() {
    return this.http.get(myUrl).subscribe((res: any) => {
        this.companiesList$.next(res.data);
    });
}

Как я могу получить доступ только к последнему переданному значению из BehaviourSubject после завершения запроса?


person shAkur    schedule 14.01.2021    source источник
comment
Как предполагалось в некоторых ответах, вам не нужно заключать Observable, предоставленный из HTTP-вызова, в другой Observable (BehaviorSubject). У вас уже есть Observable, с которым вы можете работать. Попробуйте декларативный подход к своим Observables. Это упростит получение уведомлений. См. Здесь: youtube.com/watch?v=Z76QlSpYcck   -  person DeborahK    schedule 15.01.2021


Ответы (4)


Возможно, вы могли бы попробовать следующее, сохранить наблюдаемый http-запрос в сервисе и применить к нему оператор shareReplay (1). Затем в вашем компоненте (ах) вы можете использовать его следующим образом:

component.ts

onClick() {
    this.service.getCompanies().subscribe(companies => {
        console.log(companies);
    });
}

service.ts

$companies =  this.http.get(myUrl).pipe(shareReplay(1));

getCompanies() {
    this.companies$;
}
person Jelle Bruisten    schedule 14.01.2021

Хотя технически вы можете использовать companiesList$.getValue(), я бы рекомендовал не создавать подписку в вашем компоненте, используя subscribe(), а вместо этого использовать AsyncPipe (ссылка). Это могло выглядеть так:

component.ts

companies$ = this.service.companiesList$.asObservable();

onClick() {
  this.service.getCompanies();
}

component.html

<div *ngIf="companies$ | async as companies">
  <!-- your stuff -->
  {{ companies.length }}
</div>
person Paweł Jacewicz    schedule 14.01.2021
comment
Я не хочу перечислять компании в шаблоне. Я хочу получить доступ к API-компаниям через companiesList $ behavioriourSubject ПОСЛЕ получения результата - person shAkur; 14.01.2021
comment
Тогда вы можете сделать что-нибудь вроде this.service.companiesList$.pipe(skip(1)).subscribe(companies => {console.log(companies.length); });. BehaviorSubject имеет начальное значение (в данном случае []), если вы skip, вы всегда должны иметь самое последнее значение в подписке. Если вам не нужно начальное значение, вы также можете использовать вместо него ReplaySubject или даже Subject. - person Paweł Jacewicz; 15.01.2021

Чтобы получить последнее значение излучения, используйте:

companiesList$.getValue().

Если вы хотите использовать его в потоке, используйте withLatestFrom оператор.

person Shlomi Levi    schedule 14.01.2021

Я думаю, что здесь не хватает ответов на то, что вы используете BehaviourSubject, а это неправильный тип темы.

Суммируя :

  • Тема будет выводить значения только с момента вашей подписки. Итак, если emit вызывается для Subject 5 раз, тогда компонент подписывается, они не увидят эти предыдущие 5 отправлений.

  • ReplaySubject будет выводить X значений сразу после подписки. Итак, если у меня есть ReplaySubject (1), и я выводю значение 5 раз, то компонент подписывается. Они немедленно получат последнее 1 значение, а затем продолжат получать дальнейшие излучения.

  • BehaviourSubject почти идентичен ReplaySubject, за исключением того, что он имеет значение по умолчанию, которое всегда будет генерироваться, независимо от того, вызывали ли вы emit для этого объекта.

В вашем коде у вас есть:

companiesList$ = new BehaviorSubject([]);

Таким образом, даже до того, как вы вызовете emit, любой, кто на это подпишется, немедленно получит пустой массив. Итак, я думаю, что читая между строк вашего вопроса, у вас возникают проблемы с получением пустого массива при подписке, затем вы получаете свои фактические данные.

Вы можете решить эту проблему, используя ReplaySubject:

companiesList$ = new ReplaySubject(1);

Которая ничего не будет выводить до первого испускания, тогда все подписчики всегда будут получать этот последний выпуск.

Здесь стоит изучить различия каждого типа темы: https://tutorialsforangular.com/2020/12/12/subject-vs-replaysubject-vs-behaviorsubject/

person MindingData    schedule 14.01.2021