Skip to content

Todo List (In-Memory State)

A simple example to help you get the basics. In practice, you'd want to persist the data. See Todo List - Server State for an example of how to retrieve from & mutate to a server.

Live islandRuns locally in this page

Page shell

<article>
  <cami-todo-list-simple></cami-todo-list-simple>
</article>
<script type="module" src="./island.js"></script>

Island source

const { html, ReactiveElement } = cami
class TaskManagerElement extends ReactiveElement {
    tasks = [];
    addTask(name) {
        this.tasks = [...this.tasks, { name, completed: false }];
    }
    removeTask(index) {
        this.tasks = this.tasks.filter((_task, taskIndex) => taskIndex !== index);
    }
    template() {
        return html `
      <input id="taskInput" type="text" placeholder="Enter task name">
      <button @click=${() => {
            const input = this.querySelector('#taskInput');
            if (!input?.value.trim())
                return;
            this.addTask(input.value.trim());
            input.value = '';
        }}>Add Task</button>
      <ul>
        ${this.tasks.map((task, index) => html `
          <li>
            ${task.name}
            <button @click=${() => this.removeTask(index)}>Remove</button>
          </li>
        `)}
      </ul>
    `;
    }
}
customElements.define('cami-todo-list-simple', TaskManagerElement);
import { html, ReactiveElement } from 'cami'

interface Task {
  name: string
  completed: boolean
}

class TaskManagerElement extends ReactiveElement {
  tasks: Task[] = []

  addTask(name: string): void {
    this.tasks = [...this.tasks, { name, completed: false }]
  }

  removeTask(index: number): void {
    this.tasks = this.tasks.filter((_task, taskIndex) => taskIndex !== index)
  }

  template(): ReturnType<typeof html> {
    return html`
      <input id="taskInput" type="text" placeholder="Enter task name">
      <button @click=${() => {
        const input = this.querySelector<HTMLInputElement>('#taskInput')
        if (!input?.value.trim()) return
        this.addTask(input.value.trim())
        input.value = ''
      }}>Add Task</button>
      <ul>
        ${this.tasks.map((task: Task, index: number) => html`
          <li>
            ${task.name}
            <button @click=${() => this.removeTask(index)}>Remove</button>
          </li>
        `)}
      </ul>
    `
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'cami-todo-list-simple': TaskManagerElement
  }
}

customElements.define('cami-todo-list-simple', TaskManagerElement)