✓ Todo List
Composition API: a static shared list + a TAB-scoped #[Signal] draft. #[Broadcast(Scope::ROUTE)] makes every action fan out to all viewers.
#[Broadcast(Scope::ROUTE)] on the class sets ROUTE as the primary scope. A bare
$ctx->broadcast() then re-renders the list for every browser on this route — no scope argument needed.Mixed state — the todo list is a plain
static array (shared across the worker, no attribute required), while the input is a TAB-scoped #[Signal] so your draft stays private.#[Action] methods mutate the static array, then call
$ctx->broadcast(). Adding clears the draft via $this->newTodo = '', which syncs back to the input automatically.Callable view — the view passes a closure so
self::$todos is re-read on every render. A string-template view would freeze the data captured at setup.cacheUpdates: false disables view caching so every broadcast re-renders the full list. Partial rendering sends only the
#todo-list block, keeping SSE payloads small.- No todos yet. Add one above!
Total: 0 items
⚡ Signals
newTodo
string TAB "" #[Signal] draft input — private per tab so your typing doesn't leak to others.
🎯 Actions
addTodo
Appends the trimmed draft to the static list, clears it, and broadcasts to all viewers.
deleteTodo
Removes a todo by ID (from $ctx->input('id')) and broadcasts the updated list.
toggleTodo
Flips the completed state of a todo and broadcasts.
👁 Views
todo.html.twig
Unchanged from the closure version. Renders only the #todo-list block on updates.