DEV Community

Diego Liascovich
Diego Liascovich

Posted on

🧠 Implementing the Memento Pattern with a Stepper Form in Angular

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:

  1. Originator: The object whose state needs to be saved and restored.
  2. Memento: A snapshot of the Originator's state.
  3. 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; } } 
Enter fullscreen mode Exit fullscreen mode

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 = []; } } 
Enter fullscreen mode Exit fullscreen mode

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); } } 
Enter fullscreen mode Exit fullscreen mode

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> 
Enter fullscreen mode Exit fullscreen mode

✅ 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)