|
1 | 1 | --- |
2 | 2 | layout: pattern.njk |
3 | 3 | title: Reactive Todo List |
4 | | -description: A reactive todo list with add, done, and remove -- using live templates, object arrays, and the new `remove` command for index-based splicing. |
| 4 | +description: A reactive todo list with add, done, and remove |
5 | 5 | tags: [reactivity, list, remove, live-template] |
6 | 6 | difficulty: intermediate |
7 | 7 | --- |
8 | 8 |
|
9 | 9 | A complete todo list: add items, mark them done, remove them, filter by |
10 | 10 | text. State is a plain array of `{text, done}` objects, rendered by a |
11 | | -`<script type="text/hyperscript-template" live>` that re-renders whenever the array changes. |
| 11 | +`<script type="text/hyperscript-template" live>` template that re-renders whenever the array changes. |
12 | 12 |
|
13 | 13 | {% example "Todo List" %} |
14 | 14 | <div class="todo-app" |
@@ -93,71 +93,66 @@ text. State is a plain array of `{text, done}` objects, rendered by a |
93 | 93 |
|
94 | 94 | All state lives in two global variables initialized on the wrapper div: |
95 | 95 |
|
96 | | -``` |
| 96 | +~~~ hyperscript |
97 | 97 | init set $todos to [{text:'Learn hyperscript', done:true}, |
98 | 98 | {text:'Build something cool', done:false}, |
99 | 99 | {text:'Ship it', done:false}] |
100 | 100 | set $search to '' |
101 | | -``` |
| 101 | +~~~ |
102 | 102 |
|
103 | 103 | `$todos` is a plain array of `{text, done}` objects. `$search` is the |
104 | | -current filter string, two-way bound to the search input via `bind`. |
| 104 | +current filter string, two-way bound to the search input via [`bind`](/features/bind). |
105 | 105 |
|
106 | 106 | ### Adding items |
107 | 107 |
|
108 | 108 | The text input listens for Enter and has a named `add` event that the |
109 | 109 | button can trigger: |
110 | 110 |
|
111 | | -``` |
| 111 | +~~~ hyperscript |
112 | 112 | on keyup[key is 'Enter'] trigger add |
113 | 113 | on add |
114 | 114 | halt unless my value is not empty |
115 | 115 | append {text: my value, done: false} to $todos |
116 | 116 | clear me |
117 | | -``` |
| 117 | +~~~ |
118 | 118 |
|
119 | 119 | `append` pushes a new object onto `$todos`. The live template sees the |
120 | 120 | mutation and re-renders. |
121 | 121 |
|
122 | 122 | ### Rendering |
123 | 123 |
|
124 | | -The `<script type="text/hyperscript-template" live>` block renders the list. `#for` iterates `$todos` |
125 | | -with a `where` filter and an `index i` binding: |
| 124 | +The `<script type="text/hyperscript-template" live>` block renders the |
| 125 | +list. `#for` iterates `$todos` with a `where` filter: |
126 | 126 |
|
127 | | -``` |
128 | | -#for todo in $todos where its text contains $search ignoring case index i |
129 | | - <li class="${'done' if todo.done else ''}"> |
| 127 | +~~~ html |
| 128 | +#for todo in $todos where the todo's text contains $search ignoring case |
| 129 | + <li class="${'done' if todo is done}"> |
130 | 130 | ... |
131 | 131 | </li> |
132 | 132 | #else |
133 | 133 | <li class="empty">Nothing here.</li> |
134 | 134 | #end |
135 | | -``` |
136 | | -
|
137 | | -The `index i` gives us the position of each item in the original |
138 | | -(unfiltered) array, which we need for the checkbox and remove button. |
| 135 | +~~~ |
139 | 136 |
|
140 | 137 | ### Toggling done |
141 | 138 |
|
142 | | -Each checkbox writes back to the source array by index: |
| 139 | +Each checkbox writes back to the todo object directly: |
143 | 140 |
|
144 | 141 | ```html |
145 | | -<input type="checkbox" _="on click set $todos[${i}].done to my checked" /> |
| 142 | +<input type="checkbox" _="on click set the todo's done to my checked" /> |
146 | 143 | ``` |
147 | 144 |
|
148 | | -`${i}` is interpolated at render time, so each checkbox targets its own |
149 | | -item. Setting the property triggers re-render, which updates the |
150 | | -`class="done"` and the strikethrough. |
| 145 | +Note that here we have inner hyperscript on the generated input, and it is able to reference `todo` as a normal variable. |
| 146 | +This is because hyperscript captures scopes/closures associated with looping in templates and makes the loop variables |
| 147 | +available in generated scripts, making scripting with hyperscript templates natural. |
151 | 148 |
|
152 | 149 | ### Removing items |
153 | 150 |
|
154 | | -The remove button uses `remove` with an index expression: |
| 151 | +The remove button uses [`remove`](/commands/remove) with the captured object reference: |
155 | 152 |
|
156 | 153 | ```html |
157 | | -<button _="on click remove $todos[${i}]">x</button> |
| 154 | +<button _="on click remove todo from $todos">x</button> |
158 | 155 | ``` |
159 | 156 |
|
160 | | -`remove $todos[${i}]` splices element `i` out of the array (a new feature |
161 | | -in 0.9.90). Because the value at that index is a plain object, not a DOM |
162 | | -node, `remove` dispatches to the array splice path instead of the DOM |
163 | | -detach path. The live template re-renders after the splice. |
| 157 | +`remove todo from $todos` finds the object by reference in the array and |
| 158 | +splices it out. The live template reactively re-renders after the splice. |
0 commit comments