Interview question
Protect an Angular editor from losing unsaved changes
Builds a typed functional canDeactivate guard and editor contract that protects dirty work until a save has actually succeeded.
TL;DR
Builds a typed functional canDeactivate guard and editor contract that protects dirty work until a save has actually succeeded.
Angular Router, functional guards, reactive forms, canDeactivate, unsaved changes, confirmation, and navigation testing.
Practice the problem like a real interview: restate, reason, implement, and test.
Implement a route-level unsaved-changes boundary for a profile editor. Navigation should continue immediately when the form is clean or the save completed. When the form has unsaved changes, ask through an injected confirmation service and return its boolean or observable result. Keep browser dialogs out of the guard so behavior is testable.
clean form -> navigation continues
dirty form + leave -> navigation continues
dirty form + stay -> navigation is cancelled
save succeeds -> form marked pristine; later navigation continues
CanDeactivateFn.import {CanDeactivateFn} from '@angular/router';
import {Observable, of} from 'rxjs';
import {inject} from '@angular/core';
export interface PendingChanges {
canLeave(): boolean | Observable<boolean>;
}
export const pendingChangesGuard: CanDeactivateFn<PendingChanges> = component =>
component.canLeave();
export class ProfileEditor implements PendingChanges {
private readonly confirmation = inject(LeaveConfirmation);
readonly form = buildProfileForm();
saving = false;
canLeave(): boolean | Observable<boolean> {
if (!this.form.dirty) return true;
return this.confirmation.confirm(
'You have unsaved changes. Leave this page?',
);
}
save(): void {
this.saving = true;
this.api.save(this.form.getRawValue()).subscribe({
next: () => { this.form.markAsPristine(); this.saving = false; },
error: () => { this.saving = false; },
});
}
}
The guard decision is O(1); user-perceived latency depends on the confirmation UI and save operation.
beforeunload policy with limited custom UI.beforeunload misses in-application route navigation.Move to the linked follow-up, next path step, prerequisite, or deeper variant.
Practice the next layer of the same subject.