How to get values from BehaviorSubject after sync click?

Viewed 385
private selectedDocuments$: BehaviorSubject<Document[]> = new BehaviorSubject<Document[]>([]);
public selectedDocumentsFilesIds$ = this.selectedDocuments$.pipe(map((docs) => docs.map((doc) => doc.iD_FILE))).pipe(shareReplay(1));

I add a new values using this:

setSelectedDocuments(documents: Document[]): void {
    this.selectedDocuments$.next(documents);
}

I try to get last emitted values from selectedDocumentsFilesIds$ inside component:

public onClick() {
    this.documentsRepository.selectedDocumentsFilesIds$.toPromise().then((ids: number[]) => {
                if (ids.length == 0) {
                    return;
                }
    });
}

Why it does not work for me, there are any errors.

But this works:

this.documentsRepository.selectedDocumentsFilesIds$.subscribe((r) => console.log(r));

I have tried this:

  public createReestr() {
        this.documentsRepository.selectedDocumentsFilesIds$
            .pipe(
                tap((ids: number[]) => {
                    if (ids.length == 0) {
                        return throwError('Message');
                    }
                }),
                filter((ids: number[]) => ids.length > 0),
                tap((ids: number[]) => (this.dialogConfig.data = { data: { ids }, width: '500px' })),
                concatMap(() =>
                    this.dialog
                        .open(DialogDocumentCreateRegisterComponent, this.dialogConfig)
                        .afterClosed()
                        .pipe(
                            filter(Boolean),
                            concatMap((data: { date: Date; ids: number[] }) =>
                                this.documentsRepository.CreateMailReestrById(data.ids, data.date),
                            ),
                            indicate(this.createReestrloading$),
                            handleResponseMessage(this.messageService),
                        ),
                ),
            )
            .subscribe(() => {}, (e) => alert(e));
    }
2 Answers

First, a handy suggestion: You don't need multiple pipe method calls, you can chain the operator function inside the pipe call.

private selectedDocuments$ = new BehaviorSubject<Document[]>([]);
public selectedDocumentsFilesIds$ = this.selectedDocuments$.pipe(
  map((docs) => docs.map((doc) => doc.iD_FILE)),
  shareReplay(1)
);

Second you shouldn't call toPromise. It waits for the Observable to complete since a promise only has one emission. The behavior subject stays open forever so the promise will never be executed.

Therefore you need to use subscribe, as you did when you updated the post.

I'm not quite sure what you're trying to accomplish, but maybe the following example will help. A subject is created for all clicks, and in the constructor a subscription is setup that gets the last value from selectedDocumentsFilesIds$ and then logs them.

private clickedSubject = new Subject();

constructor() {
  this.clickedSubject.pipe(
     withLatestFrom(this.documentsRepository.selectedDocumentsFilesIds$),
     tap((ids) => console.log(ids))
  ).subscribe();
}

public onClick() {
  this.clickedSubject.next();
}

When using .toPromise(), your Observable becomes a promise, and will now just emit one value, and then be done.

On the other hand, when you subscribe, your subscription will continue getting data back from the Observable on each emission until you unsubscribe.

This is the fundamental difference between Promises and Observables (one async event vs several async events over time).

Related