Skip to content

Nested Updates

For deeply nested data, you can use the update method to update it. By default, other mutations available in Cami such as push, assign, setter methods, and others can only update the top level of an object.

Live islandRuns locally in this page

Page shell

<article>
  <h5>User Info with Deeply Nested Data</h5>
  <nested-data-be></nested-data-be>
</article>
<script src="./build/cami.cdn.js"></script>
<!-- CDN version below -->
<!-- <script src="https://unpkg.com/cami@0.4/build/cami.cdn.js"></script> -->
<script type="module" src="./island.js"></script>

Island source

const { html, ReactiveElement } = cami
const john = {
    name: 'John',
    age: 30,
    address: {
        street: '123 Main St',
        city: 'Anytown',
        country: 'USA',
        postalCode: '12345',
        coordinates: { lat: '40.7128', long: '74.0060' },
    },
};
const jane = {
    name: 'Jane',
    age: 31,
    address: {
        street: '456 Elm St',
        city: 'Othertown',
        country: 'Canada',
        postalCode: '67890',
        coordinates: { lat: '51.5074', long: '0.1278' },
    },
};
class NestedObservableElement extends ReactiveElement {
    user = structuredClone(john);
    changeUser() {
        this.user = structuredClone(this.user.name === 'John' ? jane : john);
    }
    changeName() {
        this.user = { ...this.user, name: this.user.name === 'John' ? 'Jane' : 'John' };
    }
    changeStreet() {
        this.user = {
            ...this.user,
            address: {
                ...this.user.address,
                street: this.user.address.street === '123 Main St' ? '456 Elm St' : '123 Main St',
            },
        };
    }
    changeLatitude() {
        this.user = {
            ...this.user,
            address: {
                ...this.user.address,
                coordinates: {
                    ...this.user.address.coordinates,
                    lat: this.user.address.coordinates.lat === '40.7128' ? '51.5074' : '40.7128',
                },
            },
        };
    }
    template() {
        return html `
      <p>Name: ${this.user.name}</p>
      <p>Street: ${this.user.address.street}</p>
      <p>Latitude: ${this.user.address.coordinates.lat}</p>
      <button @click=${() => this.changeUser()}>Change User</button>
      <button @click=${() => this.changeName()}>Change Name</button>
      <button @click=${() => this.changeStreet()}>Change Street</button>
      <button @click=${() => this.changeLatitude()}>Change Latitude</button>
    `;
    }
}
customElements.define('nested-data-be', NestedObservableElement);
import { html, ReactiveElement } from 'cami'

interface Coordinates {
  lat: string
  long: string
}

interface Address {
  street: string
  city: string
  country: string
  postalCode: string
  coordinates: Coordinates
}

interface User {
  name: string
  age: number
  address: Address
}

const john: User = {
  name: 'John',
  age: 30,
  address: {
    street: '123 Main St',
    city: 'Anytown',
    country: 'USA',
    postalCode: '12345',
    coordinates: { lat: '40.7128', long: '74.0060' },
  },
}

const jane: User = {
  name: 'Jane',
  age: 31,
  address: {
    street: '456 Elm St',
    city: 'Othertown',
    country: 'Canada',
    postalCode: '67890',
    coordinates: { lat: '51.5074', long: '0.1278' },
  },
}

class NestedObservableElement extends ReactiveElement {
  user: User = structuredClone(john)

  changeUser(): void {
    this.user = structuredClone(this.user.name === 'John' ? jane : john)
  }

  changeName(): void {
    this.user = { ...this.user, name: this.user.name === 'John' ? 'Jane' : 'John' }
  }

  changeStreet(): void {
    this.user = {
      ...this.user,
      address: {
        ...this.user.address,
        street: this.user.address.street === '123 Main St' ? '456 Elm St' : '123 Main St',
      },
    }
  }

  changeLatitude(): void {
    this.user = {
      ...this.user,
      address: {
        ...this.user.address,
        coordinates: {
          ...this.user.address.coordinates,
          lat: this.user.address.coordinates.lat === '40.7128' ? '51.5074' : '40.7128',
        },
      },
    }
  }

  template(): ReturnType<typeof html> {
    return html`
      <p>Name: ${this.user.name}</p>
      <p>Street: ${this.user.address.street}</p>
      <p>Latitude: ${this.user.address.coordinates.lat}</p>
      <button @click=${() => this.changeUser()}>Change User</button>
      <button @click=${() => this.changeName()}>Change Name</button>
      <button @click=${() => this.changeStreet()}>Change Street</button>
      <button @click=${() => this.changeLatitude()}>Change Latitude</button>
    `
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'nested-data-be': NestedObservableElement
  }
}

customElements.define('nested-data-be', NestedObservableElement)