-
-
Save matthewp/7aab5d7d46074376f8c810c5c4ba422b to your computer and use it in GitHub Desktop.
can-element full todoMVC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let todoAlgebra = new can.set.Algebra( | |
set.props.boolean("complete"), | |
set.props.id("id"), | |
set.props.sort("sort") | |
); | |
let todoStore = can.fixture.store([{ | |
name: "mow lawn", | |
complete: false, | |
id: 5 | |
}, | |
{ | |
name: "dishes", | |
complete: true, | |
id: 6 | |
}, | |
{ | |
name: "learn canjs", | |
complete: false, | |
id: 7 | |
} | |
], todoAlgebra); | |
can.fixture("/api/todos", todoStore); | |
can.fixture.delay = 1000; | |
class Todo extends can.Object { | |
constructor(props) { | |
super(props); | |
if (!this.hasOwnProperty("complete")) { | |
this.complete = false; | |
} | |
} | |
} | |
class TodoList extends can.Array { | |
get active() { | |
return this.filter(function(todo) { | |
return todo.complete === false; | |
}); | |
} | |
get complete() { | |
return this.filter(function(todo) { | |
return todo.complete === true; | |
}); | |
} | |
get allComplete() { | |
return this.length === this.complete.length; | |
} | |
get saving() { | |
return this.filter(function(todo) { | |
return todo.isSaving(); | |
}); | |
} | |
updateCompleteTo(value) { | |
this.forEach(function(todo) { | |
todo.complete = value; | |
todo.save(); | |
}); | |
} | |
destroyComplete() { | |
this.complete.forEach(function(todo) { | |
todo.destroy(); | |
}); | |
} | |
} | |
can.connect.baseMap({ | |
url: "/api/todos", | |
Map: Todo, | |
List: TodoList, | |
name: "todo", | |
algebra: todoAlgebra | |
}); | |
can.element({ | |
tag: "todo-create", | |
view: ` | |
<input id="new-todo" | |
placeholder="What needs to be done?" | |
value:bind="todo.name" | |
on:enter="createTodo()"/> | |
`, | |
data: { | |
todo: () => new Todo() | |
}, | |
methods: { | |
createTodo() { | |
this.todo = new Todo(); | |
} | |
} | |
}); | |
can.element({ | |
tag: "todo-list", | |
view: ` | |
<ul id="todo-list"> | |
{{#each(todos)}} | |
<li class="todo {{#if(./complete)}}completed{{/if}} | |
{{#if( isDestroying() )}}destroying{{/if}} | |
{{#if(../isEditing(this))}}editing{{/if}}"> | |
<div class="view"> | |
<input class="toggle" type="checkbox" checked:bind="complete"> | |
<label on:dblclick="../edit(this)">{{name}}</label> | |
<button class="destroy" on:click="destroy()"></button> | |
</div> | |
<input class="edit" type="text" | |
value:bind="name" | |
on:enter="../updateName()" | |
focused:from="../isEditing(this)" | |
on:blur="../cancelEdit()"/> | |
</li> | |
{{/each}} | |
</ul> | |
`, | |
methods: { | |
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; | |
} | |
} | |
}); | |
let TodoMVC = can.element({ | |
tag: "todo-mvc", | |
props: { | |
todoList: { | |
enumerable: false, | |
value: null, | |
configurable: true, | |
writable: true | |
} | |
}, | |
view: ` | |
<section id="todoapp"> | |
<header id="header"> | |
<h1>todos</h1> | |
<todo-create/> | |
</header> | |
<section id="main" class=""> | |
<input id="toggle-all" type="checkbox" | |
checked:bind="allChecked" | |
disabled:from="todosList.saving.length"/> | |
<label for="toggle-all">Mark all as complete</label> | |
<todo-list todos:from="todosPromise.value"/> | |
</section> | |
<footer id="footer" class=""> | |
<span id="todo-count"> | |
<strong>{{todosPromise.value.active.length}}</strong> items left | |
</span> | |
<ul id="filters"> | |
<li> | |
<a href="{{routeUrl(filter=undefined)}}" | |
{{#routeCurrent(filter=undefined)}}class='selected'{{/routeCurrent}}>All</a> | |
</li> | |
<li> | |
<a href="{{routeUrl(filter='active')}}" | |
{{#routeCurrent(filter='active')}}class='selected'{{/routeCurrent}}>Active</a> | |
</li> | |
<li> | |
<a href="{{routeUrl(filter='complete')}}" | |
{{#routeCurrent(filter='complete')}}class='selected'{{/routeCurrent}}>Completed</a> | |
</li> | |
</ul> | |
<button id="clear-completed" | |
on:click="todosList.destroyComplete()"> | |
Clear completed ({{todosPromise.value.complete.length}}) | |
</button> | |
</footer> | |
</section> | |
`, | |
computed: { | |
todosPromise() { | |
console.log("todosPromise"); | |
if (!this.filter) { | |
return Todo.getList({}); | |
} else { | |
return Todo.getList({ | |
complete: this.filter === "complete" | |
}); | |
} | |
}, | |
allChecked() { | |
return this.todosList && this.todosList.allComplete; | |
}, | |
allChecked(newVal) { | |
this.todosList && this.todosList.updateCompleteTo(newVal); | |
} | |
}, | |
lifecycle: { | |
connected() { | |
this.listenTo("todosPromise", (promise) => { | |
promise.then((todos) => { | |
this.todosList = todos; | |
}); | |
}); | |
this.todosPromise.then((todos) => { | |
console.log(JSON.stringify(todos)); | |
this.todosList = todos; | |
}); | |
return this.stopListening.bind(this); | |
} | |
} | |
}); | |
let app = new TodoMVC(); | |
route.data = app; | |
route("{filter}"); | |
route.start(); | |
document.body.appendChild(app); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment