Todo List (Attribute Data)
This is useful when you render an HTML page using a server template engine like Handlebars, ERB, or Jinja. You can pass data from the server to the client using attributes upon page load. Alternatively, you can also pass data asynchronously (see Todo List - Server State).
Live islandRuns locally in this page
Page shell
<cami-todo-list-from-attributes
todos='{"data": ["Buy milk", "Buy eggs", "Buy bread"]}'
></cami-todo-list-from-attributes>
</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
class MyComponent extends ReactiveElement {
todos = [];
onConnect() {
this.observableAttributes({
todos: (value) => JSON.parse(value).data,
});
}
addTodo(todo) {
this.todos = [...this.todos, todo];
}
deleteTodo(todo) {
this.todos = this.todos.filter((candidate) => candidate !== todo);
}
template() {
return html `
<input id="newTodo" type="text" placeholder="Enter todo title" />
<button @click=${() => {
const input = this.querySelector('#newTodo');
if (!input?.value.trim())
return;
this.addTodo(input.value.trim());
input.value = '';
}}>Add Todo</button>
<ul>
${this.todos.map((todo) => html `
<li>${todo} <button @click=${() => this.deleteTodo(todo)}>Remove</button></li>
`)}
</ul>
`;
}
}
customElements.define('cami-todo-list-from-attributes', MyComponent);
import { html, ReactiveElement } from 'cami'
interface TodoPayload {
data: string[]
}
class MyComponent extends ReactiveElement {
todos: string[] = []
onConnect(): void {
this.observableAttributes({
todos: (value: string): string[] => (JSON.parse(value) as TodoPayload).data,
})
}
addTodo(todo: string): void {
this.todos = [...this.todos, todo]
}
deleteTodo(todo: string): void {
this.todos = this.todos.filter((candidate) => candidate !== todo)
}
template(): ReturnType<typeof html> {
return html`
<input id="newTodo" type="text" placeholder="Enter todo title" />
<button @click=${() => {
const input = this.querySelector<HTMLInputElement>('#newTodo')
if (!input?.value.trim()) return
this.addTodo(input.value.trim())
input.value = ''
}}>Add Todo</button>
<ul>
${this.todos.map((todo: string) => html`
<li>${todo} <button @click=${() => this.deleteTodo(todo)}>Remove</button></li>
`)}
</ul>
`
}
}
declare global {
interface HTMLElementTagNameMap {
'cami-todo-list-from-attributes': MyComponent
}
}
customElements.define('cami-todo-list-from-attributes', MyComponent)