// 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; }