Component Edit page
Edit todo names (can-stache-bindings)
The problem
Make it possible to edit a todos
name by
double-clicking its label which should reveal
a focused input element. If the user hits
the enter key, the todo should be updated on the
server. If the input loses focus, it should go
back to the default list view.
This functionality should be encapsulated by a <todo-list {todos} />
custom element. It should accept a todos
property that
is the list of todos that will be managed by the custom element.
What you need to know
The can-stache-bindings presentation on data bindings.
The focused custom attribute can be used to specify when an element should be focused:
focused:from="shouldBeFocused()"
Use key:from to pass a value from the scope to a component:
<some-component nameInComponent:from="nameInScope" />
this can be used to get the current context in stache:
<div on:click="doSomethingWith(this)" />
The solution
Click to see the solution
Create components/todo-list/todo-list.stache as follows:
<!-- components/todo-list/todo-list.stache -->
<ul id="todo-list">
{{# for(todo of this.todos) }}
<li class="todo {{# if(todo.complete) }}completed{{/ if }}
{{# if( todo.isDestroying() ) }}destroying{{/ if }}
{{# if(this.isEditing(todo)) }}editing{{/ if }}">
<div class="view">
<input class="toggle" type="checkbox"
checked:bind="todo.complete"
on:change="todo.save()"
disabled:from="todo.isSaving()" />
<label on:dblclick="this.edit(todo)">{{ todo.name }}</label>
<button class="destroy" on:click="todo.destroy()"></button>
</div>
<input class="edit" type="text"
value:bind="todo.name"
on:enter="this.updateName()"
focused:from="this.isEditing(todo)"
on:blur="this.cancelEdit()" />
</li>
{{/ for }}
</ul>
Create components/todo-list/todo-list.js as follows:
// components/todo-list/todo-list.js
import {Component} from "can";
import view from "./todo-list.stache";
import Todo from "~/models/todo";
export default Component.extend({
tag: "todo-list",
view,
ViewModel: {
todos: Todo.List,
editing: Todo,
backupName: "string",
isEditing(todo) {
return todo === this.editing;
},
edit(todo) {
this.backupName = todo.name;
this.editing = todo;
},
cancelEdit() {
if (this.editing) {
this.editing.name = this.backupName;
}
this.editing = null;
},
updateName() {
this.editing.save();
this.editing = null;
}
}
});
Update index.stache to the following:
<!-- index.stache -->
<can-import from="~/components/todo-create/" />
<can-import from="~/components/todo-list/" />
<section id="todoapp">
<header id="header">
<h1>{{ this.appName }}</h1>
<todo-create/>
</header>
<section id="main" class="">
<input id="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<todo-list todos:from="this.todosList" />
</section>
<footer id="footer" class="">
<span id="todo-count">
<strong>{{ this.todosList.active.length }}</strong> items left
</span>
<ul id="filters">
<li>
<a class="selected" href="#!">All</a>
</li>
<li>
<a href="#!active">Active</a>
</li>
<li>
<a href="#!completed">Completed</a>
</li>
</ul>
<button id="clear-completed">
Clear completed ({{ this.todosList.complete.length }})
</button>
</footer>
</section>