Knockout-js-todo-list-sortable-1

Building a Todo app with Knockout.js

This is my first attempt with Knockout-js to build a Todo App.
Knockout is a popular JavaScript library that simplifies the creation of complex data-based user interfaces. It can be used alone or with other libraries, such as jQuery. Its primary purpose is to bind UI elements to an underlying data model defined as a JavaScript object, such that when changes are made to the UI, the model is updated, and vice versa. Knockout facilitates the use of a Model-View-ViewModel (MVVM) pattern in a web application’s client-side behavior. The two main concepts one must learn when working with Knockout’s MVVM implementation are Observables and Bindings.

Lets start.You can follow the steps from the github repo for this tutorial.

  1. ##Demo
  2. ##Download
  3. ##Practice

TodoListApp

If you are unfamiliar with this library, then I would strongly recommend reading the complete tutorial on Tutorial.

Thinking in components

Todo List ToDo application is to record the agency matters, saved it in local storage. User can add, modify, view, update, and delete todo items in application.

According to the above described To-do applications have to know this basic CRUD (Create-Read-Update-Delete) function, but also can store to-do function.

According to these requirement, I create to-do application with Knockout.js

Step1 – build ViewModel

The first step is create to-do object and then building ViewModel based on to-do object

Todo object

var Todo = function(title, completed){
    this.title = ko.observable(title);               //To-do Title
    this.completed = ko.observable(completed);       //To-do status True: completed False: uncomplete
    this.editing = ko.observable(false);             //Edit staut True: can edit False: cannot edit 
};

It is to save to-do item in array
ViewModel:

var ViewModel = function(todos){
    this.todos = ko.observableArray(todos.map(function(todo){
    return new Todo(todo.title, todo.completed);
    }));
};

Step1 – Add function

介面

![TodoListApp](/images/knockoutjs_1_1.png)

index.html:

The “current” variable is bind to input elements : The value binding links the associated DOM element’s value with a property on your view model. When the user edit form controls time, view model corresponding attribute value is automatically updated. Similarly, when you update the view model properties when the corresponding element values ​​are automatically updated on the page.
value binding has a main parameter (Value) and other parameters (valueUpdate)

<input class="todoField" id="new-todo" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add" placeholder="What needs to be done?" autofocus>

valueUpdate

If your binding also includes a parameter called valueUpdate, this defines additional browser events KO should use to detect changes besides the change event. The following string values are the most commonly useful choices:

“input” - updates your view model when the value of an or

ou can also trigger updates based on other events by using the valueUpdate parameter described above.。 reference : http://knockoutjs.com/documentation/value-binding.html

When the user presses Enter, it will invoke add function. This will create a new Todo and placed in the list in add function.

App.js:
this.current = ko.observable();

this.add = function(){
    var current = this.current().trim();
    if(current){
        this.todos.push(new Todo(current,false));
        this.current('');
    }
}.bind(this);

Step2 – Display Todo list

在显示待办事项时,用三个控件(checkbox、lable、button)组成一个待办事项控件,而在每一个控件也绑定一个:
checkbox : 让用户决定是否已完成
lable : 显示待办事项标题
button : 让用户可以删除待办事项

在knockoutjs可以使用迴圈方式来建立控件,也就是样板的概念,所以绑定ul时我用foreach方式来建立待办事项控件。

介面

![TodoListApp](/images/knockoutjs_1_2.png)

index.html:

<ul id="todo-list" data-bind="foreach: todos()">
    <li data-bind="css: { completed: completed, editing: editing }">
        <div class="view">
            <input class="todoCheckbox" data-bind="checked: completed" type="checkbox" >
            <label data-bind="text: title, event: { dblclick: $root.editItem }, css: {textDecoration:completed()==true}" ></label>
            <button class="close pull-right" data-bind="click: $root.remove">&times;</button>
        </div>
        <input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', enterKey: $root.saveEditing, escapeKey: $root.cancelEditing, selectAndFocus: editing, event: { blur: $root.saveEditing }">
    </li>
</ul>

Step2 – Edit 、 Update and Delete Todo item

To-Do control is to bind complete, edit, update, and delete functions.

Binding editing function (editItem), I use a double click event: dblclick: $ root.editItem in label elemen, when the user double-clicks the control, label label will disappear and input label appear, and this is also bound to input label on renewal and cancellation empty function.
Binding completed ToDo (completed),It is bound to checked event: checked: completed in input element, at the same time update the view model, I have given in the label’s CSS also bind css: {textDecoration: completed () == true}, so when the line is completed to-do will be deleted.
Binding delete function (remove), It is bound to click event: click: $ root.remove in button element

index.html:

<ul id="todo-list" data-bind="foreach: todos()">
    <li data-bind="css: { completed: completed, editing: editing }">
        <div class="view">
            <input class="todoCheckbox" data-bind="checked: completed" type="checkbox" >
            <label data-bind="text: title, event: { dblclick: $root.editItem }, css: {textDecoration:completed()==true}" ></label>
            <button class="close pull-right" data-bind="click: $root.remove">&times;</button>
        </div>
        <input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', enterKey: $root.saveEditing, escapeKey: $root.cancelEditing, selectAndFocus: editing, event: { blur: $root.saveEditing }">
    </li>
</ul>

App.js:

// delete function
this.remove = function(todo){
    this.todos.remove(todo);
}.bind(this);

// edit function    
this.editItem = function(item){
    item.editing(true);
    item.previousTitle = item.title();
}.bind(this);

// save function    
this.saveEditing = function(item){
    item.editing(false);

    var title = item.title();
    var trimmedTitle = title.trim();

    if(title !== trimmedTitle){
        item.title(trimmedTitle);
    }

    if(!trimmedTitle){
        this.remove(item);
    }
}.bind(this);

// cancel function
this.cancelEditing = function(item){
    item.editing(false);
    item.title(item.previousTitle);
}.bind(this);

// save function    
ko.computed(function(){
    localStorage.setItem('todos-knockoutjs', ko.toJSON(this.todos));
}.bind(this)).extend({
    rateLimit: {timeout: 500, method: 'notifyWhenChangesStop'}
});

When you finish , you can see the TodoApp as below .
TodoListApp
The complete working demo can be found here .

##Building a Todo app with Knockout.js (1) - A simple todo list

##Building a Todo app with Knockout.js (2) - Filter、Search and Sortable function