3

I an using ngrx/data in my app and am trying to figure out how to use the error$ stream to display an error in a component. Basically I am using a modal pop up with a form to create an entity and I am using the error$ stream to display any error that occurs when saving it. The problem I am having is that if an error occurs and it is displayed and the pop up is closed the next time it is opened the previous error is displayed. This is the code for the error$ selector

    this.entityActionErrors$ = actions.pipe(
      filter(
        (ea: EntityAction) =>
          ea.payload &&
          ea.payload.entityOp &&
          ea.payload.entityOp.endsWith(OP_ERROR)
      ),
      shareReplay(1)
    );

The problem is with the shareReplay(1). I am trying to figure how to ignore or filter out the previous error when the modal is reopened and the error$ stream is subscribed to again.

Is there a way in rxjs to ignore or filter out the previous emission from a sharedReplay subject?

Andrew Alderson
  • 968
  • 5
  • 16
  • Maybe the [distinctUntilChanged](https://www.learnrxjs.io/operators/filtering/distinctuntilchanged.html) operator could be useful here? `this.modalErrors$ = this.entityActionErrors$.pipe(distinctUntilChanged())` – Andrei Gătej Dec 12 '19 at 22:47
  • I have tried that and it doesn't work. The ```shareReplay``` subject is for late subscribers that need the previously emitted value from the stream. I am trying to filter out that value if it exists. I have also tried using ```skip(1)``` but that doesn't work either. – Andrew Alderson Dec 12 '19 at 22:55
  • Hmm. What if after you close the modal you emit a **null error** and then you could add a `filter` operator so that whenever another error is emitted, the consumer will be able to receive it successfully? – Andrei Gătej Dec 12 '19 at 23:53
  • I am doing something similar now. I have a separate reducer that handles the state of saving the entity and when the modal is closed I dispatch a reset action. This adds extra code that I don't think is needed. I think a better solution (which I am trying now) is to have a separate Subject that emits whenever the error$ stream emits. This solves the late subscriber issue with the shareReplay subject. – Andrew Alderson Dec 13 '19 at 00:31
  • Interesting. Please share the solution when you find it! – Andrei Gătej Dec 13 '19 at 00:34

2 Answers2

4

The solution I came up with was to use a separate Subject for the error message. I did this in the service class I created for the entity.

export class VolunteersService extends EntityCollectionServiceBase<Volunteer> {
  constructor(factory: EntityCollectionServiceElementsFactory) {
    super('Volunteer', factory);

    this.errors$
      .pipe(
        filter(
          (ea: EntityAction) =>
            ea.payload.entityName === 'Volunteer' &&
            ea.payload.entityOp === EntityOp.SAVE_ADD_ONE_ERROR
        )
      )
      .subscribe(() => this._addErrorSubject.next('We couldn\'t save your changes at the moment. Please try again later.'));
  }

  private _addErrorSubject = new Subject<string>();

  get addError$(): Observable<string> {
    return this._addErrorSubject;
  }
}

The problem I was trying to solve was the problem of local component state so while this does work I decided on a different solution. In ngrx/data all of the command methods in the EntityCollectionServiceBase class return an Observable that emits after the store has been updated. So in the component I subscribe to the command method like:

  save() {
    this._pending = true;
    this.volunteersService.add(volunteer).subscribe(
      () => {
        this._pending = false;
      },
      error => {
        this._pending = false;
        this._error = error.message;
      }
    );
  }

This solution works perfectly and keeps this state in the component where it belongs and out of the store.

Andrew Alderson
  • 968
  • 5
  • 16
3

Skipping stored value is simple:
errors$.pipe(skipUntil(timer(0)));.
It works because stored value is provided synchronously.
You can also play with asyncScheduler.

TJL
  • 6,330
  • 8
  • 34
  • 36
awarche
  • 31
  • 1