← Observer lesson
Lab

Observer

Sign in to run your code

Read the brief and the starter here. With a free account you can edit it, run the sample, step through it and check your work, and your progress is kept.

type Listener = (quantity: number) => void;
type Registration = { id: number; listener: Listener };

// The lesson's cart, keeping its registrations in a list.
class Cart {
  private quantity = 0;
  private listeners: Registration[] = [];
  private nextId = 1;

  getQuantity(): number {
    return this.quantity;
  }

  subscribe(listener: Listener): number {
    const id = this.nextId++;
    this.listeners.push({ id, listener });
    return id;
  }

  unsubscribe(id: number): void {
    this.listeners = this.listeners.filter((registration) => registration.id !== id);
  }

  setQuantity(quantity: number): void {
    if (!Number.isInteger(quantity) || quantity < 0 || quantity > 9) return;
    if (quantity === this.quantity) return;
    this.quantity = quantity;
    for (const registration of [...this.listeners]) registration.listener(quantity);
  }

  registrations(): number {
    return this.listeners.length;
  }
}

// The cart panel: the shopper opens and closes it.
class CartPanel {
  renders: number[] = [];

  open(cart: Cart): void {
    this.renders.push(cart.getQuantity()); // the cart doesn't replay, so read it
    cart.subscribe((quantity) => this.renders.push(quantity));
  }

  close(cart: Cart): void {
    // Closing slides the panel out of view.
  }
}

// Checker plumbing: runs one script against a page. You don't need to edit it.
function runCart(script: string[]): string {
  const cart = new Cart();
  // The header badge: subscribed for as long as the page is open.
  const badge = [cart.getQuantity()];
  cart.subscribe((quantity) => badge.push(quantity));
  const panel = new CartPanel();
  for (const step of script) {
    switch (step) {
      case 'open': panel.open(cart); break;
      case 'close': panel.close(cart); break;
      default: cart.setQuantity(Number(step));
    }
  }
  const views = `badge [${badge.join(' ')}] · panel [${panel.renders.join(' ')}]`;
  return `${views} · registrations ${cart.registrations()}`;
}