8

I have a confirm/cancel modal dialog that pops up when a user leaves a route. I do this by using a guard with the canDeactivate method. However I want canDeactivate to wait until it gets a response from the modal before returning anything.

I have tried to do this by returning an observable but it is not working.

canDeactivate(): Observable<boolean> | boolean {
    if(this.isFormStarted()) {
        this.formService.showExitModal(true);
        return this.formService.getModalSelectionObservable();
    }
    else {
        return true;
    }
}

Nothing is happening when I click confirm even though I can see that the observable is working fine when I do a console.log inside the if block

this.formService.getModalSelectionObservable().subscribe(
        value => console.log("dialog value: " + value)
    );

Here is how the form service looks.

private modalConfirmation = new Subject<boolean>();

public setModalSelectionObservable(confirmLeave: boolean) {
    this.modalConfirmation.next(confirmLeave);
}
public getModalSelectionObservable(): Observable<boolean> {
    return this.modalConfirmation.asObservable();
}
Mark Letters
  • 293
  • 5
  • 14
  • why is that you are using 2 different services in your `canDeactivate` function: `this.formService` and `this.createFormService`? – Harry Ninh Jul 22 '16 at 10:33
  • Yeah they should be the same, I've updated it, thanks. I just changed the variable names so that they would make more sense here. – Mark Letters Jul 22 '16 at 13:17
  • Hi Mark, how do manage to make your modal waits the user response? could you share your solution? I'm facing the same problem. Kind regards – Elmer Dantas Feb 10 '17 at 16:12
  • I have a service that sets and gets observables. When clicking confirm I call setModalSelectionObservable() and pass in true. I then subscribe to getModalSelectionObservable() in my guard service as you can see from the question. – Mark Letters Feb 13 '17 at 10:49

3 Answers3

7

Use take(1) or first() (don't forget to import)

return this.formService.getModalSelectionObservable().first();

to ensure the observable is closed after the first event, otherwise the router will wait until it is closed from the service.

Günter Zöchbauer
  • 490,478
  • 163
  • 1,733
  • 1,404
  • Hi Gunter, could you explain me how `getModalSelectionObservable` should work? I'm facing the same problem and I don't know how to make the refresh waits the result from modal. – Elmer Dantas Feb 10 '17 at 14:03
  • I don't know about `getModalSelectionObservable()`. I only know about `canDeactivate` – Günter Zöchbauer Feb 10 '17 at 15:33
  • I see...I'm facing this problem with canDeactivate...I'm not able to show custom messages or dialog using service... [see my question](http://stackoverflow.com/questions/42142053/candeactivate-confirm-message) ..could help me? – Elmer Dantas Feb 10 '17 at 15:35
  • Maybe is it a typo?: *"Use take(1) (don't forget to import)"*... but you suggest `.first()`. – developer033 Jul 09 '17 at 20:10
  • Thanks for the hint. They both work. I changed to code example from take to first at some point, but forgot to change the text as well. – Günter Zöchbauer Jul 09 '17 at 20:11
1

Just putting this here in case someone in future is as careless as me:

If your component has a function hasUnsavedChanges() your canDeactivate() method would need to return !hasUnsavedChanges().

But then if you start using an observable for hasUnsavedChanges, you'll be returning !hasUnsavedChanges$ which will just be a falsey value.

If you need to support both you can do this:

canDeactivate(component: C)
{
    var hasUnsavedChanges = component.hasUnsavedChanges();

    if (typeof (hasUnsavedChanges) === 'boolean')
    {
        return !hasUnsavedChanges;
    }
    else
    {
        return hasUnsavedChanges.map(x => !x);
    }
}
Simon_Weaver
  • 120,240
  • 73
  • 577
  • 618
1

It was not working on my end because I was using BehaviorSubject with initial value null.

Make sure to create Observable from Subject like:

private modalConfirmation = new Subject<boolean>();
public getModalSelectionObservable(): Observable<boolean> {
    return this.modalConfirmation.asObservable();
}
Bal Krishna Jha
  • 3,122
  • 20
  • 31