How to make Angular canDeactivate Service wait for Modal Dialog response?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Angular's canDeactivate route guard prevents navigation away from a component when there are unsaved changes. To show a confirmation modal and wait for the user's response, the guard must return an Observable<boolean> or Promise<boolean> instead of a synchronous boolean. The modal service emits the user's choice (confirm or cancel), and the guard subscribes to it, allowing or blocking navigation based on the response.
The Guard Interface
The guard delegates to the component's canDeactivate() method, which decides whether to show a modal.
The Modal Confirmation Service
afterClosed() returns an Observable that emits once when the dialog closes, then completes. The guard receives this Observable and waits for the user's response.
The Confirmation Dialog Component
The Component with Unsaved Changes
Route Configuration
Without Angular Material (Custom Modal)
If you are not using Material, create a simple modal with a Subject:
Angular 16+ Functional Guard
Angular 16 introduced functional guards as an alternative to class-based guards:
Common Pitfalls
- Returning a synchronous boolean when a modal is needed: If
canDeactivate()returnstrueorfalsebefore the modal opens, navigation proceeds immediately without waiting. Return anObservable<boolean>that completes when the user responds. - Not using
take(1)or completing the Observable: The router subscribes to the Observable and waits for it to emit. If your Observable never emits (e.g., the Subject is not triggered), navigation hangs indefinitely. Usetake(1)to ensure exactly one emission. - Modal appearing on every navigation: Check
form.dirtyor a similar flag before showing the modal. If the form has not been modified, returntrueimmediately without opening the dialog. - Browser back button bypassing the guard: The
canDeactivateguard handles in-app navigation. For browser back/refresh/close, also listen to thebeforeunloadevent:@HostListener('window:beforeunload', ['$event']) unloadNotification(event) { if (this.form.dirty) event.returnValue = true; }. disableClosenot set on the dialog: WithoutdisableClose: true, clicking the backdrop closes the dialog and emitsundefined, which the guard interprets asfalse(navigation blocked). Either setdisableCloseor handleundefinedexplicitly in themapoperator.
Summary
- The
canDeactivateguard returnsObservable<boolean>to wait for the modal response - The modal service opens a dialog and returns
dialogRef.afterClosed()as the Observable - The component checks
form.dirtybefore showing the modal — skip if no changes - Register the guard in the route configuration with
canDeactivate: [CanDeactivateGuard] - Add a
beforeunloadlistener for browser back/refresh/close events - Use
take(1)on custom Subject-based modals to prevent hanging navigation
Related reading
- How to make different calls to an async method waiting for the result of first call?
- How to make JavaScript execute after page load?
- How to make join queries using Sequelize on Node.js
- How to make layout with rounded corners..?
- how to make synchronous call to indexeddb method from javascript
- how to make synchronous http calls within async.each in nodejs
- How to make the corners of a button round?
- How to make Twitter Bootstrap menu dropdown on hover rather than click
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.