Knockout-js-todo-list-sortable-cn-1

Building a Todo app with Knockout.js

这是我首次尝试用Knockout-JS建立一个待办事项应用。Knockout-JS是一个JavaScript库,它简化了複杂的基于数据的用户界面的创建。它可以单独使用或与其他库,如jQuery的使用。它的主要目的是UI元素绑定到定义为JavaScript对象的底层数据模型,例如,当更改了UI製作,模型更新,反之亦然。
Knockout有助于在Web应用程序的客户端行为使用模型 - 视图 - 视图模型(MVVM -Model-View-ViewModel)模式。与Knockout的MVVM执行工作时,一个人必须学会的两个主要概念是观测(Observables)和绑定(Bindings)。。

让我们开始.您可以按照从我的GitHub库的步骤本教程

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

TodoListApp

如果你不熟悉KnockoutJS,那麽我会强烈建议阅读Knockout完整的Tutorial教程.

Thinking in components

Todo List待办事项应用的功能是记录代办事项,简单到不需要本地存储,所有的代办事项都只是存储在内存中,就是只有程序打开的时候可以增加、修改、查看、更新和删除代办事项

根据以上叙述可以知道这个待办事项应用要有基本的CRUD (Create-Read-Update-Delete)功能,还要能储存代办事项功能。

我就根据这样的需求来建立Knockout.js待办事项应用程序

Step1 – build ViewModel

建设Knockout.js应用的主要先建立ViewModel,建立ViewModel之前先将Todo项目转成物件。

Todo object

var Todo = function(title, completed){
    this.title = ko.observable(title);               //待办事项标题
    this.completed = ko.observable(completed);       //待办事项是否完成 True: 完成 False: 未完成
    this.editing = ko.observable(false);             //是否编辑中 True: 编辑中 
};

我用Array方式来存放待办事项
ViewModel:

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

Step1 – Add function

宣告一个变数current并绑定在input控件 : value 绑定是关联DOM元素的值到view model的属性上。当用户编辑表单控件的时候, view model对应的属性值会自动更新。同样,当你更新view model属性的时候,相对应的元素值在页面上也会自动更新。
value 绑定有一个主要参数(Value)和其他参数(valueUpdate)

介面

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

index.html:

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

valueUpdate

如果你使用valueUpdate参数,那就是意味着KO将使用自定义的事件而不是默认的离开焦点事件。下面是一些最常用的选项:

“change”(默认值) - 当失去焦点的时候更新view model的值,或者是

“keyup” – 当用户敲完一个字符以后立即更新view model。

“keypress” – 当用户正在敲一个字符但没有释放键盘的时候就立即更新view model。不像 keyup,这个更新和keydown是一样的。

“afterkeydown” – 当用户开始输入字符的时候就更新view model。主要是捕获浏览器的keydown事件或异步handle事件。

上述这些选项,如果你想让你的view model进行实时更新,使用“afterkeydown”是最好的选择。 更多的请参考http://knockoutjs.com/documentation/value-binding.html

当用户按下Enter时,就会呼叫add function,在此会建立新的Todo并放入清单中。

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

在待办事项控件裡,要绑定完成待办事项、编辑、更新及删除功能。

绑定编辑功能(editItem)时,我使用double click 事件 : dblclick: $root.editItem,当用户双击控件时,label控件会消失而input控件会出现,而这个input控件上也绑定了更新与取消空功能。
绑定完成待办事项(completed),绑定在checked 事件 : checked: completed,在同时更新view model时,我也特定在label的CSS也绑定css: {textDecoration:completed()==true},所以当完成待办事项时就会出现删除线。
绑定删除功能(remove),綁定在click 事件 :click: $root.remove

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'}
});

当你完成,你可以看到 TodoApp.
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