TodoMVC with Web and snippets

This page walks through the TodoMVC variant written with Web and snippets. It shows the same model as TodoMVC with Silk, and most of its view is that of TodoMVC with Web. The two differ in how a todo gets its markup. Here the markup comes from the page, as a snippet, and the view builds nothing of it. This page shows only what differs. It builds on Snippets, markup from the page too. The Silk side of the same idea is TodoMVC with Silk and snippets.

The page

web-snippets.html is silk-snippets.html with its own title, its own bundle, and its own class to start, WebSnippetsTodoApp. Its list holds the item of the template as the snippet todo, with the label as its caret. That page shows the markup. Domite takes the item off the page when Amber starts.

The view

The view is the package TodoMVC-Web-Snippets, with one class, WebSnippetsTodoApp. It imports 'amber/web/Web-Snippets' beside Web, which makes each snippet a tag message of the canvas. It keeps the todos under todos-amber-web-snippets. It attaches, binds, routes and renders with the methods of WebTodoApp, all but renderList. It has no class for one todo. The list is rendered as a block over the canvas, and each todo puts a copy of the snippet:

renderList
	listBrush contents: [ :html |
		todoList shownTodos do: [ :each | self renderTodo: each on: html ] ]
renderTodo: aTodo on: html
	| label item toggle |
	label := html todo.
	label with: aTodo title.
	item := label asDomNode closest: 'li'.
	item classList toggle: 'completed' force: aTodo isCompleted.
	toggle := item querySelector: '.toggle'.
	toggle checked: aTodo isCompleted.
	(TagBrush fromElement: toggle) onChange: [
		todoList toggle: aTodo.
		self render ].
	label onDblClick: [ self startEditing: aTodo in: item ].
	(TagBrush fromElement: (item querySelector: '.destroy')) onClick: [
		todoList remove: aTodo.
		self render ]

html todo puts a copy of the snippet into the list and answers a brush on its caret, the label. with: puts the title into it. That brush is all Web gives back. Silk answers the whole item as the frame of the caret, and Web has no such word. So the view goes from the label to the item with closest:, through the DOM. It finds the parts inside the item with querySelector:. Each part that binds is wrapped in a brush with TagBrush fromElement:. The copy is in the list before the view sets its state. So the class goes by classList, and the checkbox by checked:. The bindings do what those of WebTodoItem do. Here the view changes the list itself, since no item object stands between.

Editing

The edit belongs to the view too. startEditing:in: takes the todo and its item. It gives the item the class editing, puts the title into the input, binds the edit and focuses the input. Web has no word for taking a handler off, so the blur handler keeps the guard of the Web variant:

bindEdit: anEdit of: aTodo in: anItem
	(TagBrush fromElement: anEdit)
		onBlur: [ (anItem classList contains: 'editing') ifTrue: [ self finishEditing: aTodo in: anItem ] ];
		onKeyDown: [ :event |
			event key = 'Enter' ifTrue: [ self finishEditing: aTodo in: anItem ].
			event key = 'Escape' ifTrue: [ self stopEditing: anItem ] ]

finishEditing:in: takes the class off, retitles through the model and renders. stopEditing: takes the class off, for Escape. The blur that follows Enter finds the class gone, and saves nothing a second time.

What each point costs

Five points of the table in TodoMVC with Web are somewhere else here. The rest are where that table puts them, in WebSnippetsTodoApp:

The specification says Where it is What it takes
Toggle, class completed renderTodo:on: one onChange:, one classList toggle:force:
Destroy button on hover renderTodo:on: one onClick:; the stylesheet shows the button
Double-click edits renderTodo:on:, startEditing:in: one onDblClick:, one method
Enter or blur saves, Escape cancels, empty destroys bindEdit:of:in:, finishEditing:in:, TodoList >> retitle:to: two bindings, one guard; the model removes the todo
Persistence under todos-amber-web-snippets start, TodoStore one key; the model saves

The snippet takes the place of the tag messages, and html todo puts the whole item. What Web gives back is a brush on the caret. The item, the checkbox and the button are reached through the DOM. TagBrush fromElement: makes a brush of each that binds. The Silk variant with snippets reaches the item with frame and the parts with at:, and past Silk three times. This one reaches past Web in renderTodo:on: four times, and in every editing method, as the Web variant does.

The tests of the view

WebSnippetsTodoAppTest, in TodoMVC-Web-Snippets-Tests, answers attach:to: as WebTodoAppTest does. It answers one message more, todoListMarkup, with the snippet of the page. TodoAppTest puts that markup into the list of its fixture. It gives the snippets of the fixture to Domite before the view attaches. Web-Snippets then installs todo on the canvas. The 25 tests are the same as for the other variants.

Running it

Open http://localhost:4000/web-snippets.html, and try it as TodoMVC with Silk says. The todos are in localStorage under todos-amber-web-snippets. The test runner of Helios runs the 25 tests of WebSnippetsTodoAppTest on the page.