Implement a CanDeactivate Guard for Unsaved Changes in Angular
This challenge focuses on creating a CanDeactivate guard in Angular. This guard is crucial for preventing users from accidentally losing unsaved data when navigating away from a form or a page that requires user input. You will implement logic to check for unsaved changes and prompt the user before allowing navigation.
Problem Description
Your task is to create a CanDeactivate guard that protects a specific route from being left if the user has unsaved changes. This is a common requirement in web applications, especially for forms where users might be entering data.
Key Requirements:
- Create a
CanDeactivateGuard: Implement a class that implements theCanDeactivateinterface from@angular/router. - Detect Unsaved Changes: Within the guard, you need a mechanism to determine if there are unsaved changes. For this challenge, we will simulate this by using a simple boolean flag within the component being protected.
- Prompt the User: If unsaved changes are detected, the guard should present a confirmation dialog (using
window.confirmfor simplicity) to the user, asking if they really want to leave. - Control Navigation:
- If the user confirms they want to leave, allow navigation.
- If the user cancels, prevent navigation.
- Apply the Guard: Configure your Angular application's routing module to apply this guard to a designated route.
- Component Integration: Create a simple component that the guard will protect. This component should have a mechanism to set and unset the "unsaved changes" flag.
Expected Behavior:
- When a user navigates to the protected route and there are no unsaved changes, navigation should proceed normally.
- When a user navigates to the protected route and there are unsaved changes, a confirmation dialog should appear.
- If the user clicks "OK" (or equivalent), navigation proceeds.
- If the user clicks "Cancel" (or equivalent), navigation is blocked, and the user remains on the current page.
- When a user navigates away from the protected route, and there are unsaved changes, the same confirmation dialog should appear.
Edge Cases to Consider:
- What happens if the component being protected hasn't been initialized yet? (The guard should handle this gracefully.)
- How does the guard behave when navigating to a child route within the protected route? (For this challenge, focus on leaving the entire protected route.)
Examples
This challenge doesn't have traditional input/output like a function. Instead, we'll describe the user experience.
Scenario 1: No Unsaved Changes
- User Action: Navigates to the protected "Edit Profile" page. No changes have been made to the form. Then, clicks a link to navigate to the "Dashboard" page.
- Expected Behavior: The user is immediately navigated to the "Dashboard" page. No confirmation dialog is shown.
Scenario 2: With Unsaved Changes - User Confirms Leaving
- User Action: Navigates to the protected "Edit Profile" page. Types some text into a profile description field (triggering the "unsaved changes" flag). Then, clicks a link to navigate to the "Dashboard" page.
- Expected Behavior:
- A confirmation dialog appears with a message like "You have unsaved changes. Are you sure you want to leave?"
- The user clicks "OK".
- The user is navigated to the "Dashboard" page.
Scenario 3: With Unsaved Changes - User Cancels Leaving
- User Action: Navigates to the protected "Edit Profile" page. Types some text into a profile description field (triggering the "unsaved changes" flag). Then, clicks a link to navigate to the "Dashboard" page.
- Expected Behavior:
- A confirmation dialog appears with a message like "You have unsaved changes. Are you sure you want to leave?"
- The user clicks "Cancel".
- The user remains on the "Edit Profile" page, and the "Dashboard" page is not loaded.
Constraints
- The solution must be implemented in TypeScript using Angular.
- Use the
@angular/routermodule for routing. - For simplicity, simulate the detection of unsaved changes using a boolean property within the component. No actual form dirty checking logic is required.
- Use
window.confirm()for displaying the confirmation prompt. - The guard should be applied to a single, designated route.
Notes
CanDeactivateInterface: TheCanDeactivateinterface has acanDeactivatemethod that takes thecomponentinstance,currentRoute,currentState,nextState, andhistoryStateas arguments.- Component Type: You will need to specify the type of the component being deactivated in the generic type parameter of
CanDeactivate<T>. - Simulating Changes: In your protected component, you'll need a public property (e.g.,
hasUnsavedChanges: boolean = false) and methods to set/unset it (e.g.,markAsDirty(),markAsPristine()). - Routing Configuration: Remember to provide the guard in your module and add it to the
canDeactivateproperty in your route configuration. - Return Type: The
canDeactivatemethod must return a boolean, an Observable<boolean>, a Promise<boolean>, or UrlTree. For this challenge, a boolean or Observable<boolean> is sufficient.
Good luck! This is a fundamental concept in building robust Angular applications.