2

I have a Service that emits events:

export class MyService {
  private event = new BehaviorSubject<string>('init');
  event$ = this.event.asObservable();

  constructor() { }

  update(): void {
    this.event.next('update');
  }

  accept(): void {
    this.event.next('accept');
  }

  decline(): void {
    this.event.next('decline');
  }
}

I also have a CanDeactivateGuard that is triggered by a function in a Component:

canDeactivate(): Observable<boolean> {
  return this.service.event$.pipe(
    map(action => {
      return action === 'accept';
    })
  )
}

Now this works all fine. But I've got an issue:

This will always return the last event. So when nothing happened it will send init immediately. If update() was called, it will send update directly.

How can I make this to work so that it either:

  • … waits until accept or decline is send?
  • … waits until the next fresh Event is emitted?
lampshade
  • 2,470
  • 3
  • 36
  • 74

2 Answers2

1

You are receiving initial events because it is a BehaviorSubject.

You are receiving all events because you are not filtering them out.

How you approach this depends on what purpose(s) event$ is serving. If it is important to emit all events, including the initial state, then definitely leave it as a behavior subject.

I would filter the events in the guard:

canDeactivate(): Observable<boolean> {
  return this.service.event$.pipe(
    filter(action => action === 'accept' || action === 'decline'),
    map(action => {
      return action === 'accept';
    })
  );
}

This has the effect of ignoring everything that isn't 'accept' or 'decline'.

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
  • Thank you so much. `filter()` did the trick. In combination with `skip(1)` this is just perfect. Thank you so much. – lampshade Feb 09 '20 at 20:36
1

You can skip the first emission from your BehaviorSubject with skip:

this.service
  .pipe(
    skip(1),
    filter(a => ['accept', 'decline'].includes(a))
  )
martin
  • 93,354
  • 25
  • 191
  • 226