Created
April 2, 2012 17:58
-
-
Save JasonMore/2285747 to your computer and use it in GitHub Desktop.
Backbone.js TODO app implemented in knockout.js
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title>Backbone.js Todos</title> | |
<link rel="stylesheet" href="todos.css"/> | |
</head> | |
<body> | |
<div id="todoapp"> | |
<header> | |
<h1>Todos</h1> | |
<input id="new-todo" type="text" placeholder="What needs to be done?"> | |
</header> | |
<section id="main"> | |
<input id="toggle-all" type="checkbox"> | |
<label for="toggle-all">Mark all as complete</label> | |
<ul id="todo-list"></ul> | |
</section> | |
<footer> | |
<a id="clear-completed">Clear completed</a> | |
<div id="todo-count"></div> | |
</footer> | |
</div> | |
<div id="instructions"> | |
Double-click to edit a todo. | |
</div> | |
<div id="credits"> | |
Created by | |
<br /> | |
<a href="http://jgn.me/">Jérôme Gravel-Niquet</a>. | |
<br />Rewritten by: <a href="http://addyosmani.github.com/todomvc">TodoMVC</a>. | |
</div> | |
<script src="../../test/vendor/json2.js"></script> | |
<script src="../../test/vendor/jquery-1.7.1.js"></script> | |
<script src="../../test/vendor/underscore-1.3.1.js"></script> | |
<script src="../../backbone.js"></script> | |
<script src="../backbone-localstorage.js"></script> | |
<!-- Templates --> | |
<script type="text/template" id="item-template"> | |
<div class="view"> | |
<input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> /> | |
<label><%= title %></label> | |
<a class="destroy"></a> | |
</div> | |
<input class="edit" type="text" value="<%= title %>" /> | |
</script> | |
<script type="text/template" id="stats-template"> | |
<% if (done) { %> | |
<a id="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a> | |
<% } %> | |
<div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div> | |
</script> | |
<script> | |
$(function(){ | |
var Todo = Backbone.Model.extend({ | |
defaults: function() { | |
return { | |
title: "empty todo...", | |
order: Todos.nextOrder(), | |
done: false | |
}; | |
}, | |
initialize: function() { | |
if (!this.get("title")) { | |
this.set({"title": this.defaults.title}); | |
} | |
}, | |
toggle: function() { | |
this.save({done: !this.get("done")}); | |
}, | |
clear: function() { | |
this.destroy(); | |
} | |
}); | |
var TodoList = Backbone.Collection.extend({ | |
model: Todo, | |
localStorage: new Store("todos-backbone"), | |
done: function() { | |
return this.filter(function(todo){ return todo.get('done'); }); | |
}, | |
remaining: function() { | |
return this.without.apply(this, this.done()); | |
}, | |
nextOrder: function() { | |
if (!this.length) return 1; | |
return this.last().get('order') + 1; | |
}, | |
comparator: function(todo) { | |
return todo.get('order'); | |
} | |
}); | |
var Todos = new TodoList; | |
var TodoView = Backbone.View.extend({ | |
tagName: "li", | |
template: _.template($('#item-template').html()), | |
events: { | |
"click .toggle" : "toggleDone", | |
"dblclick .view" : "edit", | |
"click a.destroy" : "clear", | |
"keypress .edit" : "updateOnEnter", | |
"blur .edit" : "close" | |
}, | |
initialize: function() { | |
this.model.bind('change', this.render, this); | |
this.model.bind('destroy', this.remove, this); | |
}, | |
render: function() { | |
this.$el.html(this.template(this.model.toJSON())); | |
this.$el.toggleClass('done', this.model.get('done')); | |
this.input = this.$('.edit'); | |
return this; | |
}, | |
toggleDone: function() { | |
this.model.toggle(); | |
}, | |
edit: function() { | |
this.$el.addClass("editing"); | |
this.input.focus(); | |
}, | |
close: function() { | |
var value = this.input.val(); | |
if (!value) this.clear(); | |
this.model.save({title: value}); | |
this.$el.removeClass("editing"); | |
}, | |
updateOnEnter: function(e) { | |
if (e.keyCode == 13) this.close(); | |
}, | |
clear: function() { | |
this.model.clear(); | |
} | |
}); | |
var AppView = Backbone.View.extend({ | |
el: $("#todoapp"), | |
statsTemplate: _.template($('#stats-template').html()), | |
events: { | |
"keypress #new-todo": "createOnEnter", | |
"click #clear-completed": "clearCompleted", | |
"click #toggle-all": "toggleAllComplete" | |
}, | |
initialize: function() { | |
this.input = this.$("#new-todo"); | |
this.allCheckbox = this.$("#toggle-all")[0]; | |
Todos.bind('add', this.addOne, this); | |
Todos.bind('reset', this.addAll, this); | |
Todos.bind('all', this.render, this); | |
this.footer = this.$('footer'); | |
this.main = $('#main'); | |
Todos.fetch(); | |
}, | |
render: function() { | |
var done = Todos.done().length; | |
var remaining = Todos.remaining().length; | |
if (Todos.length) { | |
this.main.show(); | |
this.footer.show(); | |
this.footer.html(this.statsTemplate({done: done, remaining: remaining})); | |
} else { | |
this.main.hide(); | |
this.footer.hide(); | |
} | |
this.allCheckbox.checked = !remaining; | |
}, | |
addOne: function(todo) { | |
var view = new TodoView({model: todo}); | |
this.$("#todo-list").append(view.render().el); | |
}, | |
addAll: function() { | |
Todos.each(this.addOne); | |
}, | |
createOnEnter: function(e) { | |
if (e.keyCode != 13) return; | |
if (!this.input.val()) return; | |
Todos.create({title: this.input.val()}); | |
this.input.val(''); | |
}, | |
clearCompleted: function() { | |
_.each(Todos.done(), function(todo){ todo.clear(); }); | |
return false; | |
}, | |
toggleAllComplete: function () { | |
var done = this.allCheckbox.checked; | |
Todos.each(function (todo) { todo.save({'done': done}); }); | |
} | |
}); | |
var App = new AppView; | |
}); | |
</script> | |
</body> | |
</html> |
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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>Knockout Demo: Todos</title> | |
<link href="todos.css" media="all" rel="stylesheet" type="text/css" /> | |
<script src="jquery-1.6.1.js" type="text/javascript"></script> | |
<script src="jquery.tmpl.js" type="text/javascript"></script> | |
<script src="knockout-1.2.1.debug.js" type="text/javascript"></script> | |
</head> | |
<body> | |
<div id="todoapp"> | |
<div class="title"> | |
<h1>Todos</h1> | |
</div> | |
<div class="content"> | |
<div id="create-todo"> | |
<input id="new-todo" data-bind="value: current, event: { keyup: add}" placeholder="What needs to be done?" | |
type="text" /> | |
<span class="ui-tooltip-top" style="display: none;">Press Enter to save this task</span> | |
</div> | |
<div id="todos"> | |
<ul id="todo-list" data-bind="template: { name: 'todoitemtemplate', foreach: todos }"> | |
</ul> | |
</div> | |
<div id="todo-stats" data-bind="template: { name: 'statstemplate'}"> | |
</div> | |
</div> | |
</div> | |
<ul id="instructions"> | |
<li>Double-click to edit a todo.</li> | |
</ul> | |
<div id="credits"> | |
Created by | |
<br /> | |
<a href="http://jgn.me/">Jérôme Gravel-Niquet</a> | |
<br /> | |
Modified to use knockout.js by | |
<br /> | |
<a href="https://github.com/ashish01/knockoutjs-todos">Ashish Sharma</a> | |
</div> | |
<script id="todoitemtemplate" type="text/html"> | |
<li> | |
<div data-bind="attr: { class : done() ? 'todo done' : 'todo'}"> | |
<div class="display"> | |
<input class="check" type="checkbox" data-bind="checked: done" /> | |
<div class="todo-content" data-bind="text: content"></div> | |
<span class="todo-destroy" data-bind="click: viewModel.remove"></span> | |
</div> | |
<div class="edit"> | |
<input class="todo-input" type="text" data-bind="value: content"/> | |
</div> | |
</div> | |
</li> | |
</script> | |
<script id="statstemplate" type="text/html"> | |
<span class="todo-count" data-bind="visible: remaining().length > 0"> | |
<span class="number" data-bind="text: remaining().length"></span> | |
<span class="word" data-bind="text: remaining().length == 1 ? 'item' : 'items'"></span> left. | |
</span> | |
<span class="todo-clear" data-bind="visible: done().length > 0"> | |
<a href="#" data-bind="click: removeCompleted"> | |
Clear <span class="number-done" data-bind="text: done().length"></span> | |
completed <span class="word-done"data-bind="text: done().length == 1 ? 'item' : 'items'"></span> | |
</a> | |
</span> | |
</script> | |
<script type="text/javascript"> | |
var Todo = function (text) { | |
this.content = ko.observable(text); | |
this.order = ko.observable(); | |
this.done = ko.observable(false); | |
} | |
var viewModel = { | |
todos: ko.observableArray(), | |
current: ko.observable(), | |
add: function (event) { | |
if (event.keyCode === 13) { | |
var newTodo = new Todo(this.current()); | |
this.todos.push(newTodo); | |
this.current(""); | |
} | |
}, | |
remove: function (event) { | |
viewModel.todos.remove(this); | |
}, | |
removeCompleted: function (event) { | |
viewModel.todos.removeAll(viewModel.done()); | |
} | |
}; | |
viewModel.remaining = ko.dependentObservable(function () { | |
return this.todos().filter(function (el) { | |
return el.done() === false; | |
}); | |
}, viewModel), | |
viewModel.done = ko.dependentObservable(function () { | |
return this.todos().filter(function (el) { | |
return el.done() === true; | |
}); | |
}, viewModel), | |
ko.applyBindings(viewModel); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment