// Views
var TaskView = Backbone.View.extend({
tagName: 'div',
className: 'task',
events: {
'change input': 'toggleComplete',
'click span': 'confirmDelete',
'click a': 'editName'
},
initialize: function() {
_.bindAll(this, 'render');
if(this.model) {
this.render();
this.model.bind('change', this.render);
}
},
render: function() {
$(this.el).html(_(TaskView.SRC).template( this.model.toJSON() ));
this.handleEvents();
if( this.model.get('complete') ) $(this.el).addClass('complete');
return this;
},
toggleComplete: function() {
var cb = $(this.el).find('input').get(0);
this.model.set({ complete:cb.checked });
this.model.save();
if(cb.checked) $(this.el).addClass('complete');
else $(this.el).removeClass('complete');
},
confirmDelete: function() {
if(confirm("This will permanently delete the task:\n\n - "+ this.model.get('name')+"\n\nIs that OK?" )) {
this.model.destroy();
}
},
editName: function() {
var newName = prompt("Edit task name:", this.model.get('name'));
if( newName != null && newName != "" ) {
this.model.set({ name:newName });
this.model.save();
}
}
});
// HTML source
TaskView.SRC = '\
<input type="checkbox" <% if(typeof complete != "undefined" && complete == true) { %>checked<% } %>/>\
<a href="#"><%= name %></a>\
<span> X </span>\
';
// Models
var Task = Backbone.Model.extend({
toJSON: function() {
return _({}).extend(_(this.attributes).clone(), { id:this.id });
}
});
var TaskCollection = Backbone.Collection.extend({
model: Task
});
window.taskCollection = new TaskCollection();
// Events
// Create a Task when the form is submitted
$('form').bind('submit', function(){
var fld = $(this).find('input[type=text]'),
value = fld.val();
if(value != null && value != "") {
taskCollection.create({ name:value, complete:false });
}
fld.val("");
return false;
});
// When a new task is added to the collection, create and append a new TaskView to the #tasks div
taskCollection.bind('add', function(task) {
var view = new TaskView({ model:task, id:"view-"+ task.id });
$('#tasks').append(view.el);
});
// When a task is removed from the collection, kill the associated DOM object
taskCollection.bind('remove', function(task) {
$('#view-'+ task.id).remove();
});
// When the collection is refreshed, blitz all the old TaskViews and create new ones
taskCollection.bind('refresh', function(col) {
$('#tasks').html("");
col.each(function(task){
var view = new TaskView({ model:task, id:"view-"+ task.id });
$('#tasks').append(view.el);
});
});
// Custom sync implementation
Backbone.old_sync = Backbone.sync;
Backbone.sync = function(method, model, success, error) {
if(method == 'create') {
model.set({ id:_.uniqueId('task-') });
}
success({ model:model });
if(window.JSON && window.localStorage) { // Saves to localstorage
window.localStorage['backbone_fiddle_todos'] = JSON.stringify(taskCollection.toArray());
}
}
if(window.JSON && window.localStorage) { // Loads from localstorage
var taskData = window.localStorage['backbone_fiddle_todos'];
if( taskData != null && taskData != "" ) {
taskData = JSON.parse(taskData);
taskCollection.refresh(taskData);
}
}
<h1>Todo List</h1>
<form>
<input type="text" placeholder="Task name">
<input type="submit" value="Add Task">
</form>
<section id="tasks">
Loading...
</section>
<footer>By M@ McCray</footer>
body {
font-family: helvetica, arial, sans-serif;
font-size: 14px;
}
h1 {
font-size: 150%;
}
#tasks {
border: 1px solid #777;
margin: 10px 0px;
}
.task {
padding: 4px 10px;
position: relative;
}
.task:nth-child(even) {
background-color: #F0F0F0;
}
.task.complete {
opacity: 0.7;
}
.task.complete a {
color: #777;
}
.task input {
}
.task a {
padding: 0px 10px;
color:blue;
}
.task span {
color: maroon;
cursor: pointer;
position: absolute;
right: 10px;
}
.task span:hover {
color: red;
}
footer {
color: silver;
padding: 10px;
text-align: center;
}