| 124 | `], |
| 125 | }) |
| 126 | export class ItemListComponent { |
| 127 | // Signal input (Angular 17+) |
| 128 | todoList = input.required<TodoList>(); |
| 129 | |
| 130 | newItemTitle = ''; |
| 131 | |
| 132 | // Computed signal that depends on todoList input |
| 133 | private todoListId = computed(() => this.todoList().id); |
| 134 | |
| 135 | // LiveQuery as signal - updates when database changes |
| 136 | // Note: liveQuery tracks Dexie reads, not signal changes. |
| 137 | // This works because @for track ensures each component instance has a fixed todoListId. |
| 138 | items = toSignal( |
| 139 | from(liveQuery(() => |
| 140 | db.todoItems.where({ todoListId: this.todoListId() }).toArray() |
| 141 | )), |
| 142 | { initialValue: [] as TodoItem[] } |
| 143 | ); |
| 144 | |
| 145 | async addItem() { |
| 146 | const title = this.newItemTitle.trim(); |
| 147 | if (!title) return; |
| 148 | await db.todoItems.add({ |
| 149 | todoListId: this.todoListId(), |
| 150 | title, |
| 151 | done: false, |
| 152 | }); |
| 153 | this.newItemTitle = ''; |
| 154 | } |
| 155 | |
| 156 | async toggleItem(item: TodoItem) { |
| 157 | await db.todoItems.update(item.id, { done: !item.done }); |
| 158 | } |
| 159 | |
| 160 | async deleteItem(id: number) { |
| 161 | await db.todoItems.delete(id); |
| 162 | } |
| 163 | |
| 164 | async deleteList() { |
| 165 | // Delete all items in this list, then the list itself (atomically) |
| 166 | await db.transaction('rw', db.todoItems, db.todoLists, async () => { |
| 167 | await db.todoItems.where({ todoListId: this.todoListId() }).delete(); |
| 168 | await db.todoLists.delete(this.todoListId()); |
| 169 | }); |
| 170 | } |
| 171 | } |