🧠 Implementing the Memento Pattern with a Stepper Form in Angular
Diego Liascovich

Diego Liascovich @padie78

About: Full Stack Developer with solid experience designing and building scalable web applications and distributed systems.Passionate about clean code, application security,and building high-quality software

Joined:
Jun 19, 2024

🧠 Implementing the Memento Pattern with a Stepper Form in Angular

Publish Date: Jul 8
0 0

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.

Comments 0 total

    Add comment