Knockout-js-todo-list-sortable-tw-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