In this article, we’ll explore a real-world use case of the Memento Design Pattern using a multi-step form (stepper) in Angular. This pattern helps manage state across steps, allowing users to go back and forth while preserving previous data snapshots.
📘 What is the Memento Pattern?
The Memento Pattern is a behavioral design pattern that allows you to save and restore the previous state of an object without exposing its implementation details. It’s often used in scenarios like undo/redo, form state management, or editor history.
🧩 Components of the Memento Pattern
The pattern consists of three main components:
- Originator: The object whose state needs to be saved and restored.
- Memento: A snapshot of the Originator's state.
- Caretaker: Manages and stores the history of Mementos but does not modify or access their internal state.
💡 Real Case: Stepper Form with Snapshot History
Imagine a multi-step checkout form (e.g., shipping info, payment details, confirmation). We want to:
- Capture the form state after each step.
- Allow the user to go back and edit previous steps.
- Restore the state exactly as it was.
🛠️ Memento Implementation in Angular
1. Create the Memento Classes
// memento.ts export class FormMemento { constructor(public readonly state: any) {} } export class FormOriginator { private state: any = {}; setState(state: any) { this.state = { ...state }; } save(): FormMemento { return new FormMemento(this.state); } restore(memento: FormMemento) { this.state = { ...memento.state }; } getState(): any { return this.state; } }
2. Caretaker Service to Store Snapshots
// memento.service.ts import { Injectable } from '@angular/core'; import { FormMemento } from './memento'; @Injectable({ providedIn: 'root' }) export class MementoService { private history: FormMemento[] = []; addSnapshot(memento: FormMemento) { this.history.push(memento); } getSnapshot(step: number): FormMemento | null { return this.history[step] || null; } clear() { this.history = []; } }
3. Stepper Component Example
// stepper.component.ts import { Component } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { FormOriginator } from './memento'; import { MementoService } from './memento.service'; @Component({ selector: 'app-stepper', templateUrl: './stepper.component.html' }) export class StepperComponent { step = 0; form: FormGroup; originator = new FormOriginator(); constructor(private fb: FormBuilder, private caretaker: MementoService) { this.form = this.fb.group({ name: [''], address: [''], payment: [''] }); } nextStep() { this.originator.setState(this.form.value); this.caretaker.addSnapshot(this.originator.save()); this.step++; } previousStep() { this.step--; const snapshot = this.caretaker.getSnapshot(this.step); if (snapshot) { this.originator.restore(snapshot); this.form.setValue(this.originator.getState()); } } submit() { console.log('Final state:', this.form.value); } }
4. Template Example
<!-- stepper.component.html --> <form [formGroup]="form"> <div *ngIf="step === 0"> <label>Name: <input formControlName="name" /></label> </div> <div *ngIf="step === 1"> <label>Address: <input formControlName="address" /></label> </div> <div *ngIf="step === 2"> <label>Payment: <input formControlName="payment" /></label> </div> <div class="buttons"> <button type="button" (click)="previousStep()" [disabled]="step === 0">Back</button> <button type="button" (click)="nextStep()" *ngIf="step < 2">Next</button> <button type="submit" (click)="submit()" *ngIf="step === 2">Submit</button> </div> </form>
✅ Benefits of This Approach
- Maintains form state cleanly across steps.
- Encapsulates logic for state saving/restoring.
- Easy to extend (e.g., with undo/redo or validation snapshots).
- Promotes separation of concerns via the Memento pattern components.
Top comments (0)