May 28, 2013

KnockoutJS - checkbox與textbox混搭應用 (1)


建立ViewModel
function ViewModel() {
    var self = this;
    self.hasOther = ko.observable(false);
}

ko.applyBindings(new ViewModel());

hasOther表示checkbox是否被勾選

設定View的bindings
<label>
    <input type="checkbox" data-bind="checked: hasOther" />Others:</label>
<input type="text" data-bind="enable: hasOther" />

checkbox裡用了checked binding,當checkbox被勾選時,ViewModel裡的hasOther會被更新成true
textbox裡則用了enable binding,當hasOther為true時,textbox就會被enabled。當然你也可以選擇用disable binding來實作。

到目前為止已達到需求的前半段 - 有勾選checkbox時,textbox才可以編輯,執行結果可參考http://jsfiddle.net/petekcchen/z4YJe/

接下來實作後半段的需求 - checkbox被勾選的話,textbox要取得focus。看到focus,會想到hasfocus binding,只要將hasfocus binding設為hasOther,就可以達到當hasOther為true時,hasfocus也會是true,textbox就會有focus。OK,將hasfocus binding加入View中(請注意,hasfocus的focus首字是小寫)。
<label>
    <input type="checkbox" data-bind="checked: hasOther" />Others:</label>
<input type="text" data-bind="enable: hasOther, hasfocus: hasOther" />

執行結果如http://jsfiddle.net/petekcchen/z4YJe/2/,勾選checkbox,嗯!不錯,textobx取得了focus。取消勾選checkbox,咦?取消不了了!

事實上是我誤會hasfocus的意思,hasfocus binding是雙向的(two-way binding),也就是說當勾選checkbox第一次時,因為checkbox將hasOther設為true了,KnockoutJS也就將focus給textbox。但第二次要取消勾選checkbox時,因為離開了textbox(失去focus),所以KnockoutJS就將hasOther馬上設為false,接著checkbox也被KnockoutJS取消勾選。但消取掉後因為此時滑鼠點擊下去了,所以又將checkbox勾選起來,自然textbox又取得focus了。

那要怎麼解決呢?相當簡單,以hasOther()來讀取ViewModel中的hasOther屬性,hasOther在focus變動時就不會寫回ViewModel了,執行結果可參考http://jsfiddle.net/petekcchen/z4YJe/3/,這種作法有點像是讓hasfocus只能唯讀取得hasOther,事實上KnockoutJS的Documentation也有提到讀取ViewModel屬性的方式(Not all browsers support JavaScript getters and setters (* cough * IE * cough *), so for compatibility, ko.observable objects are actually functions.)。另一種可能的做法則是透過subscribe函式訂閱hasOther屬性的變動,在函式內判斷當新的值為true時,設定textbox取得focus。

No comments: