Knockout應用開發指南

第一章:入門javascript

一、Knockout簡介 (Introduction)css

Knockout是一個輕量級的UI類庫,經過應用MVVM模式使JavaScript前端UI簡單化。html

Knockout有以下4大重要概念:前端

聲明式綁定 (Declarative Bindings):使用簡明易讀的語法很容易地將模型(model)數據關聯到DOM元素上。java

UI界面自動刷新 (Automatic UI Refresh):當您的模型狀態(model state)改變時,您的UI界面將自動更新。node

依賴跟蹤 (Dependency Tracking):爲轉變和聯合數據,在你的模型數據之間隱式創建關係。jquery

模板 (Templating):爲您的模型數據快速編寫複雜的可嵌套的UI。git

簡稱:KOgithub

官方網站:http://knockoutjs.comweb

二、入門介紹 (Getting started)

2.1KO工做原理及帶來的好處

Knockout是一個以數據模型(data model)爲基礎的可以幫助你建立富文本,響應顯示和編輯用戶界面的JavaScript類庫。任什麼時候候若是你的UI須要自動更新(好比:更新依賴於用 戶的行爲或者外部數據源的改變),KO可以很簡單的幫你實現而且很容易維護。

重要特性:

優雅的依賴追蹤- 無論任什麼時候候你的數據模型更新,都會自動更新相應的內容。

聲明式綁定- 淺顯易懂的方式將你的用戶界面指定部分關聯到你的數據模型上。

靈活全面的模板- 使用嵌套模板能夠構建複雜的動態界面。

輕易可擴展- 幾行代碼就能夠實現自定義行爲做爲新的聲明式綁定。

額外的好處:

純JavaScript類庫 – 兼容任何服務器端和客戶端技術

可添加到Web程序最上部 – 不須要大的架構改變

簡潔的 – Gzip以前大約25kb

兼容任何主流瀏覽器 (IE 6+、Firefox 2+、Chrome、Safari、其它)

Comprehensive suite of specifications (採用行爲驅動開發) - 意味着在新的瀏覽器和平臺上能夠很容易經過驗證。

開發人員若是用過Silverlight或者WPF可能會知道KO是MVVM模式的一個例子。若是熟悉 Ruby on Rails 或其它MVC技術可能會發現它是一個帶有聲明式語法的MVC實時form。換句話說,你能夠把KO當成經過編輯JSON數據來製做UI用戶界面的一種方 式… 無論它爲你作什麼

OK, 如何使用它?

簡單來講:聲明你的數據做爲一個JavaScript 模型對象(model object),而後將DOM 元素或者模板(templates)綁定到它上面.

讓咱們來看一個例子

想一想在一個頁面上,航空旅客能夠爲他們的旅行升級高級食物套餐,當他們選擇一個套餐的時候,頁面當即顯示套餐的描述和價格。首先,聲明可用的套餐:

var availableMeals = [
    { mealName: 'Standard', description: 'Dry crusts of bread', extraCost: 0 },
    { mealName: 'Premium', description: 'Fresh bread with cheese', extraCost: 9.95 },
    { mealName: 'Deluxe', description: 'Caviar and vintage Dr Pepper', extraCost: 18.50 }
  ];

若是想讓這3個選項顯示到頁面上,咱們能夠綁定一個下拉菜單(例如:<select>元素)到這個數據上。例如:

<h3>Meal upgrades</h3>
<p>Make your flight more bearable by selecting a meal to match your social and economic status.</p>
Chosen meal: <select data-bind="options: availableMeals,
                                optionsText: 'mealName'"></select>

啓用Knockout並使你的綁定生效,在availableMeals變量聲明以後添加以下代碼:

var viewModel = {
    /* we'll populate this in a moment */
};

ko.applyBindings(viewModel); // Makes Knockout get to work
// 注意:ko. applyBindings須要在上述HTML以後應用纔有效

你能夠在這個系列裏將學習更多的view model 和 MVVM。如今你的頁面將render成以下的樣子:

 

 

響應選擇

下一步,聲明一個簡單的data model來描述旅客已經選擇的套餐,添加一個屬性到當前的view model上:

var viewModel = {
    chosenMeal: ko.observable(availableMeals[0])
};

ko.observable是什麼?它是KO裏的一個基礎概念。UI能夠監控(observe)它的值而且迴應它的變化。這裏咱們設置chosenMeal是UI能夠監控已經選擇的套餐,並初始化它,使用availableMeal裏的第一個值做爲它的默認值(例如:Standard)。

讓咱們將chosenMeal 關聯到下拉菜單上,僅僅是更新<select>的data-bind屬性,告訴它讓<select>元素的值讀取/寫入chosenMeal這個模型屬性:

Chosen meal: <select data-bind="options: availableMeals,
                                optionsText: 'mealName',
                                value: chosenMeal"></select>

理論上說,咱們如今能夠讀/寫chosenMeal 屬性了,可是咱們不知道它的做用。讓咱們來顯示已選擇套餐的描述和價格:

<p>
    You've chosen:
    <b data-bind="text: chosenMeal().description"></b>
    (price: <span data-bind='text: chosenMeal().extraCost'></span>)
</p>

因而,套餐信息和價格,將根據用戶選擇不一樣的套餐項而自動更新:

 

更多關於observables和dependency tracking的使用

最後一件事:若是能將價格格式化成帶有貨幣符號的就行了,聲明一個JavaScript函數就能夠實現了…

function formatPrice(price) {
    return price == 0 ? "Free" : "$" + price.toFixed(2);
}

… 而後更新綁定信息使用這個函數 …

(price: <span data-bind='text: formatPrice(chosenMeal().extraCost)'></span>)

… 界面顯示結果將變得好看多了:

 

Price的格式化展現了,你能夠在你的綁定裏寫任何JavaScript代碼,KO仍然能探測到你的綁定依賴代碼。這就展現了當你的model改變時,KO如何只進行局部更新而不用從新render整個頁面 – 僅僅是有依賴值改變的那部分。

 

鏈式的observables也是支持的(例如:總價依賴於價格和數量)。當鏈改變的時候,依賴的下游部分將會從新執行,同時全部相關的UI將自動更新。不須要在各個observables之間聲明關聯關係,KO框架會在運行時自動執行的。

你能夠從 observables 和 observable arrays 獲取更多信息。上面的例子很是簡單,沒有覆蓋不少KO的功能。你能夠獲取更多的內嵌的綁定和模板綁定。

 

KO和jQuery (或Prototype等)是競爭關係仍是能一塊兒使用?

全部人都喜歡jQuery! 它是一個在頁面裏操做元素和事件的框架,很是出色而且易使用,在DOM操做上確定使用jQuery,KO解決不一樣的問題。

若是頁面要求複雜,僅僅使用jQuery須要花費更多的代碼。 例如:一個表格裏顯示一個列表,而後統計列表的數量,Add按鈕在數據行TR小於5調的時候啓用,不然就禁用。jQuery 沒有基本的數據模型的概念,因此須要獲取數據的數量(從table/div或者專門定義的CSS class),若是須要在某些SPAN裏顯示數據的數量,當添加新數據的時候,你還要記得更新這個SPAN的text。固然,你還要判斷當總數>=5條的時候禁用Add按鈕。 而後,若是還要實現Delete功能的時候,你不得不指出哪個DOM元素被點擊之後須要改變。

 

Knockout的實現有何不一樣?

使用KO很是簡單。將你的數據描繪成一個JavaScript數組對象myItems,而後使用模板(template)轉化這個數組到表格裏(或者一組DIV)。無論何時數組改變, UI界面也會響應改變(不用指出如何插入新行<tr>或在哪裏插入),剩餘的工做就是同步了。例如:你能夠聲明綁定以下一個SPAN顯示數據數量(能夠放在頁面的任何地方,不必定非要在template裏):

There are <span data-bind="text: myItems().count"></span> items

就是這些!你不須要寫代碼去更新它,它的更新依賴於數組myItems的改變。一樣, Add按鈕的啓用和禁用依賴於數組myItems的長度,以下:

<button data-bind="enable: myItems().count < 5">Add</button>

以後,若是你要實現Delete功能,沒必要指出如何操做UI元素,只須要修改數據模型就能夠了。

 

總結:KO沒有和jQuery或相似的DOM 操做API對抗競爭。KO提供了一個關聯數據模型和用戶界面的高級功能。KO自己不依賴jQuery,可是你能夠一塊兒同時使用jQuery, 生動平緩的UI改變須要真正使用jQuery。

2.2下載安裝

Knockout的核心類庫是純JavaScript代碼,不依賴任何第三方的類庫。因此按照以下步驟便可添加KO到你的項目裏:

下載Knockout類庫的最新版本,在正式開發和產品使用中,請使用默認的壓縮版本(knockout-x.x.js)。
下載地址:https://github.com/SteveSanderson/knockout/downloads

Debug調試目的,可以使用非壓縮版本(knockout-x.x.debug.js). 和壓縮版本一樣的功能,可是具備全變量名和註釋的可讀性源代碼,而且沒有隱藏內部的API。

在你的HTML頁面裏使用<script>標籤引用Knockout類庫文件。

這就是你須要的一切…

開啓模板綁定

…除非你想使用模板綁定功能(您頗有可能使用它,由於很是有用),那你須要再引用兩個JavaScript文件。 KO1.3版的默認模板引擎是依賴jQuery 的jquery.tmpl.js(最新版2.0版已經不依賴jquery tmp了)。因此你須要下載下面的2個文件並在引用KO以前引用:

jQuery 1.4.2 或更高版本

jquery-tmpl.js — 此版本 能夠很容易使用,或者你訪問官方網站 查找最新版本。

正確的引用順序:

<script type='text/javascript' src='jquery-1.4.2.min.js'></script>
<script type='text/javascript' src='jquery-tmpl.js'></script>
<script type='text/javascript' src='knockout-1.2.1.js'></script>

(固然,您要根據你的文件路徑累更新上面的文件路徑和文件名。)

Knockout應用開發指南 第二章:監控屬性(Observables)

關於Knockout的3個重要概念(Observables,DependentObservables,ObservableArray),本人沒法準確表達它的準確含義,因此暫定翻譯爲(監控屬性、依賴監控屬性和監控數組),若是有好的建議請指正,多謝。

一、建立帶有監控屬性的view model

Observables

Knockout是在下面三個核心功能是創建起來的:

監控屬性(Observables)和依賴跟蹤(Dependency tracking)

聲明式綁定(Declarative bindings)

模板(Templating)

 這一節,你講學到3個功能中的第一個。 在這以前, 咱們來解釋一下MVVM模式和view model的概念。

 MVVM and View Models

Model-View-View Model (MVVM) 是一種建立用戶界面的設計模式。 描述的是如何將複雜的UI用戶界面分紅3個部分:

 model: 你程序裏存儲的數據。這個數據包括對象和業務操做(例如:銀子帳戶能夠完成轉帳功能), 而且獨立於任何UI。使用KO的時候,一般說是向服務器調用Ajax讀寫這個存儲的模型數據。

 view model: 在UI上,純code描述的數據以及操做。例如,若是你實現列表編輯,你的view model應該是一個包含列表項items的對象和暴露的add/remove列表項(item)的操做方法。

    注意這不是UI自己:它不包含任何按鈕的概念或者顯示風格。它也不是持續數據模型 – 包含用戶正在使用的未保存數據。使用KO的時候,你的view models是不包含任何HTML知識的純JavaScript 對象。保持view model抽象能夠保持簡單,以便你能管理更復雜的行爲。

 view: 一個可見的,交互式的,表示view model狀態的UI。 從view model顯示數據,發送命令到view model(例如:當用戶click按鈕的時候) ,任何view model狀態改變的時候更新。

使用KO的時候,你的view就是你帶有綁定信息的HTML文檔,這些聲明式的綁定管理到你的view model上。或者你可使用模板從你的view model獲取數據生成HTML。

建立一個view model,只須要聲明任意的JavaScript object。例如:

var myViewModel = {
    personName: 'Bob',
    personAge: 123
};

你能夠爲view model建立一個聲明式綁定的簡單view。例如:下面的代碼顯示personName 值:

The name is <span data-bind="text: personName"></span>

 

Activating Knockout

data-bind屬性儘快好用但它不是HTML的原生屬性(它嚴格聽從HTML5語法, 雖然HTML4驗證器提示有不可識別的屬性但依然可用)。因爲瀏覽器不識別它是什麼意思,因此你須要激活Knockout 來讓他起做用。

激活Knockout,須要添加以下的 <script> 代碼塊:

ko.applyBindings(myViewModel);

你能夠將這個代碼塊放在HTML底部,或者放在jQuery的$函數或者ready 函數裏,而後放在頁面上面, 最終生成結果就是以下的HTML代碼:

The name is <span>Bob</span>

你可能奇怪ko.applyBindings使用的是什麼樣的參數,

 第一個參數是你想用於聲明式綁定

第二個參數(可選),能夠聲明成使用data-bind的HTML元素或者容器。例如, ko.applyBindings(myViewModel, document.getElementById('someElementId'))。它的如今是隻有做爲someElementId 的元素和子元素才能激活KO功能。 好處是你能夠在同一個頁面聲明多個view model,用來區分區域。

 

Observables

如今已經知道如何建立一個簡單的view model而且經過binding顯示它的屬性了。可是KO一個重要的功能是當你的view model改變的時候能自動更新你的界面。當你的view model部分改變的時候KO是如何知道的呢?答案是:你須要將你的model屬性聲明成observable的, 由於它是很是特殊的JavaScript objects,可以通知訂閱者它的改變以及自動探測到相關的依賴。

 

例如:將上述例子的view model改爲以下代碼:

var myViewModel = {
    personName: ko.observable('Bob'),
    personAge: ko.observable(123)
};

你根本不須要修改view – 全部的data-bind語法依然工做,不一樣的是他能監控到變化,當值改變時,view會自動更新。

 

監控屬性(observables)的讀和寫

不是全部的瀏覽器都支持JavaScript的 getters and setters (好比IE),,因此爲了兼容性,使用ko.observable監控的對象都是真實的function函數。

 讀取監控屬性(observable)的值,只須要直接調用監控屬性(observable)(不須要參數),例如myViewModel.personName() 將返回'Bob', myViewModel.personAge() 將返回 123。

寫一個新值到監控屬性(observable)上,調用這個observable屬性並當新值做爲參數。例如:調用 myViewModel.personName('Mary') 將更新name值爲'Mary'。

給一個model對象的多個屬性寫入新值,你可使用鏈式語法。例如: myViewModel.personName('Mary').personAge(50) 將會將name更新爲 'Mary' 而且 將age更新爲 50.

監控屬性(observables)的特徵就是監控(observed),例如其它代碼能夠說我須要獲得對象變化的通知,因此KO內部有不少內置的綁定語法。因此若是你的代碼寫成data-bind="text: personName", text綁定註冊到自身,一旦personName的值改變,它就能獲得通知。

固然調用myViewModel.personName('Mary')改變name的值,text綁定將自動更新這個新值到相應的DOM元素上。這就是如何將view model的改變傳播到view上的。

 

監控屬性(Observables)的顯式訂閱

一般狀況下,你不用手工訂閱,因此新手能夠忽略此小節。高級用戶,若是你要註冊本身的訂閱到監控屬性(observables),你能夠調用它的subscribe 函數。例如:

myViewModel.personName.subscribe(function (newValue) {
    alert("The person's new name is " + newValue);
});

這個subscribe 函數在內部不少地方都用到的。你也能夠終止本身的訂閱:首先獲得你的訂閱,而後調用這個對象的dispose函數,例如:

var subscription = myViewModel.personName.subscribe(function (newValue) { /* do stuff */ });
// ...then later...
subscription.dispose(); // I no longer want notifications

大多數狀況下,你不須要作這些,由於內置的綁定和模板系統已經幫你作好不少事情了,能夠直接使用它們。

 

二、使用依賴監控屬性(Dependent Observables)

若是你已經有了監控屬性firstName和lastName,你想顯示全稱怎麼辦? 這就須要用到依賴監控屬性了 – 這些函數是一個或多個監控屬性, 若是他們的依賴對象改變,他們會自動跟着改變。

例如,下面的view model,

var viewModel = {
    firstName: ko.observable('Bob'),
    lastName: ko.observable('Smith')
};

… 你能夠添加一個依賴監控屬性來返回姓名全稱:

viewModel.fullName = ko.dependentObservable(function () {
    return this.firstName() + " " + this.lastName();
}, viewModel);

而且綁定到UI的元素上,例如:

The name is <span data-bind="text: fullName"></span>

… 無論firstName仍是lastName改變,全稱fullName都會自動更新(無論誰改變,執行函數都會調用一次,無論改變成什麼,他的值都會更新到UI或者其餘依賴監控屬性上)。

 

管理‘this’

新手可忽略此小節,你只須要安裝上面例子中的代碼模式寫就好了,無需知道/關注這個this。

你可能疑惑ko.dependentObservable的第二個參數是作什麼用的(上面的例子中我傳的是viewModel), 它是聲明執行依賴監控屬性的this用的。 沒有它,你不能引用到this.firstName() 和this.lastName()。 老練的JavaScript 開發人員不以爲this怎麼樣,可是若是你不熟悉JavaScript,那就對它就會很陌生。(C#和Java須要不須要爲set一個值爲設置this,可是JavaScript 須要,由於默認狀況下他們的函數自身不是任何對象的一部分)。

 

不幸的是, JavaScript 對象沒有任何辦法能引用他們自身,因此你須要經過myViewModelObject.myDependentObservable = ... 的形式添加依賴監控屬性到view model對象上。 你不能直接在view model裏聲明他們,換句話說,你不能寫成下面這樣:

var viewModel = {
    myDependentObservable: ko.dependentObservable(function() {
        ...
    }, /* can't refer to viewModel from here, so this doesn't work */)
}

… 相反你必須寫成以下這樣:

var viewModel = {
    // Add other properties here as you wish
};

viewModel.myDependentObservable = ko.dependentObservable(function() {
    ...
}, viewModel); // This is OK

只要你知道指望什麼,它確實不是個問題。J

 

依賴鏈

理所固然,若是你想你能夠建立一個依賴監控屬性的鏈。例如:

監控屬性items表述一組列表項

監控屬性selectedIndexes保存着被用戶選上的列表項的索引

依賴監控屬性selectedItems 返回的是selectedIndexes 對應的列表項數組

另外一個依賴監控屬性返回的true或false依賴於 selectedItems 的各個列表項是否包含一些屬性(例如,是否新的或者還未保存的)。一些UI element(像按鈕的啓用/禁用)的狀態取決於這個值)。

 而後,items或者selectedIndexes 的改變將會影響到全部依賴監控屬性的鏈,全部綁定這些屬性的UI元素都會自動更新。多麼整齊與優雅!

 

可寫的依賴監控屬性

新手可忽略此小節,可寫依賴監控屬性真的是太advanced了,並且大部分狀況下都用不到。

正如所學到的,依賴監控屬性是經過計算其它的監控屬性而獲得的。感受是依賴監控屬性正常狀況下應該是隻讀的。那麼,有可能讓依賴監控屬性支持可寫麼?你只須要聲明本身的callback函數而後利用寫入的值再處理一下相應的邏輯便可。

你能夠像使用普通的監控屬性同樣使用依賴監控屬性 – 數據雙向綁定到DOM元素上,而且經過自定義的邏輯攔截全部的讀和寫操做。這是很是牛逼的特性而且能夠在大範圍內使用。

 

例1:分解用戶的輸入

返回到經典的「first name + last name = full name」 例子上,你可讓事情調回來看: 讓依賴監控屬性fullName可寫,讓用戶直接輸入姓名全稱,而後輸入的值將被解析並映射寫入到基本的監控屬性firstName和lastName上:

var viewModel = {
    firstName: ko.observable("Planet"),
    lastName: ko.observable("Earth")
};

viewModel.fullName = ko.dependentObservable({

    read: function () {
        return this.firstName() + " " + this.lastName();
    },

    write: function (value) {
        var lastSpacePos = value.lastIndexOf(" ");
        if (lastSpacePos > 0) { // Ignore values with no space character
            this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
            this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
        }
    },
    owner: viewModel
});

這個例子裏,寫操做的callback接受寫入的值,把值分離出來,分別寫入到「firstName」和「lastName」上。 你能夠像普通狀況同樣將這個view model綁定到DOM元素上,以下:

<p>First name: <span data-bind="text: firstName"></span></p>
<p>Last name: <span data-bind="text: lastName"></span></p>
<h2>Hello, <input data-bind="value: fullName"/>!</h2>

這是一個Hello World 例子的反例子,姓和名都不可編輯,相反姓和名組成的姓名全稱倒是可編輯的。

上面的view model演示的是經過一個簡單的參數來初始化依賴監控屬性。你能夠給下面的屬性傳入任何JavaScript對象:

   read — 必選,一個用來執行取得依賴監控屬性當前值的函數。

   write — 可選,若是聲明將使你的依賴監控屬性可寫,別的代碼若是這個可寫功能寫入新值,經過自定義邏輯將值再寫入各個基礎的監控屬性上。

   owner — 可選,若是聲明,它就是KO調用read或write的callback時用到的this。查看「管理this」獲取更新信息。

 例2:Value轉換器

 有時候你可能須要顯示一些不一樣格式的數據,從基礎的數據轉化成顯示格式。好比,你存儲價格爲float類型,可是容許用戶編輯的字段須要支持貨幣單位和小數點。你能夠用可寫的依賴監控屬性來實現,而後解析傳入的數據到基本 float類型裏:

viewModel.formattedPrice = ko.dependentObservable({

    read: function () {
        return "$" + this.price().toFixed(2);
    },

    write: function (value) {
        // Strip out unwanted characters, parse as float, then write the raw data back to the underlying "price" observable
        value = parseFloat(value.replace(/[^\.\d]/g, ""));
        this.price(isNaN(value) ? 0 : value); // Write to underlying storage
    },
    owner: viewModel
});

而後咱們綁定formattedPrice到text box上:

<p>Enter bid price: <input data-bind="value: formattedPrice"/></p>

因此,無論用戶何時輸入新價格,輸入什麼格式,text box裏會自動更新爲帶有2位小數點和貨幣符號的數值。這樣用戶能夠看到你的程序有多聰明,來告訴用戶只能輸入2位小數,不然的話自動刪除多餘的位數,固然也不能輸入負數,由於write的callback函數會自動刪除負號。

 

例3:過濾並驗證用戶輸入

例1展現的是寫操做過濾的功能,若是你寫的值不符合條件的話將不會被寫入,忽略全部不包括空格的值。

再多走一步,你能夠聲明一個監控屬性isValid 來表示最後一次寫入是否合法,而後根據真假值顯示相應的提示信息。稍後仔細介紹,先參考以下代碼:

var viewModel = {
    acceptedNumericValue: ko.observable(123),
    lastInputWasValid: ko.observable(true)
};

viewModel.attemptedValue = ko.dependentObservable({
    read: viewModel.acceptedNumericValue,
    write: function (value) {
        if (isNaN(value))
            this.lastInputWasValid(false);
        else {
            this.lastInputWasValid(true);
            this.acceptedNumericValue(value); // Write to underlying storage
        }
    },
    owner: viewModel
});

… 按照以下格式聲明綁定元素:

<p>Enter a numeric value: <input data-bind="value: attemptedValue"/></p>
<div data-bind="visible: !lastInputWasValid()">That's not a number!</div>

如今,acceptedNumericValue 將只接受數字,其它任何輸入的值都會觸發顯示驗證信息,而會更新acceptedNumericValue。

備註:上面的例子顯得殺傷力太強了,更簡單的方式是在<input>上使用jQuery Validation和number class。Knockout能夠和jQuery Validation一塊兒很好的使用,參考例子:grid editor 。固然,上面的例子依然展現了一個如何使用自定義邏輯進行過濾和驗證數據,若是驗證很複雜而jQuery Validation很難使用的話,你就能夠用它。

 

依賴跟蹤如何工做的

新手不必知道太清楚,可是高級開發人員能夠須要知道爲何依賴監控屬性可以自動跟蹤而且自動更新UI…

事實上,很是簡單,甚至說可愛。跟蹤的邏輯是這樣的:

當你聲明一個依賴監控屬性的時候,KO會當即調用執行函數而且獲取初始化值。

當你的執行函數運行的時候,KO會把全部須要依賴的依賴屬性(或者監控依賴屬性)都記錄到一個Log列表裏。

執行函數結束之後,KO會向全部Log裏須要依賴到的對象進行訂閱。訂閱的callback函數是從新運行你的執行函數。而後回頭從新執行上面的第一步操做(而且註銷再也不使用的訂閱)。

最後KO會通知上游全部訂閱它的訂閱者,告訴它們我已經設置了新值。

全部說,KO不只僅是在第一次執行函數執行時候探測你的依賴項,每次它都會探測。舉例來講,你的依賴屬性能夠是動態的:依賴屬性A表明你是否依賴於依賴屬性B或者C,這時候只有當A或者你當前的選擇B或者C改變的時候執行函數才從新執行。你不須要再聲明其它的依賴:運行時會自動探測到的。

另一個技巧是:一個模板輸出的綁定是依賴監控屬性的簡單實現,若是模板讀取一個監控屬性的值,那模板綁定就會自動變成依賴監控屬性依賴於那個監控屬性,監控屬性一旦改變,模板綁定的依賴監控屬性就會自動執行。嵌套的模板也是自動的:若是模板X render模板 Y,而且Y須要顯示監控屬性Z的值,當Z改變的時候,因爲只有Y依賴它,因此只有Y這部分進行了從新繪製(render)。

三、使用observable數組

若是你要探測和響應一個對象的變化,你應該用observables。若是你須要探測和響應一個集合對象的變化,你應該用observableArray 。在不少場景下,它都很是有用,好比你要在UI上須要顯示/編輯的一個列表數據集合,而後對集合進行添加和刪除。

var myObservableArray = ko.observableArray();    // Initially an empty array
myObservableArray.push('Some value');            // Adds the value and notifies observers

 

關鍵點:監控數組跟蹤的是數組裏的對象,而不是這些對象自身的狀態。

簡單說,將一對象放在observableArray 裏不會使這個對象自己的屬性變化可監控的。固然你本身也能夠聲明這個對象的屬性爲observable的,但它就成了一個依賴監控對象了。一個observableArray 僅僅監控他擁有的對象,並在這些對象添加或者刪除的時候發出通知。

 

預加載一個監控數組observableArray

若是你想讓你的監控數組在開始的時候就有一些初始值,那麼在聲明的時候,你能夠在構造器里加入這些初始對象。例如:

// This observable array initially contains three objects
var anotherObservableArray = ko.observableArray([
    { name: "Bungle", type: "Bear" },
    { name: "George", type: "Hippo" },
    { name: "Zippy", type: "Unknown" }
]);

 

從observableArray裏讀取信息

一個observableArray其實就是一個observable的監控對象,只不過他的值是一個數組(observableArray還加了不少其餘特性,稍後介紹)。因此你能夠像獲取普通的observable的值同樣,只須要調用無參函數就能夠獲取自身的值了。 例如,你能夠像下面這樣獲取它的值:

alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);

理論上你可使用任何原生的JavaScript數組函數來操做這些數組,可是KO提供了更好的功能等價函數,他們很是有用是由於:

兼容全部瀏覽器。(例如indexOf不能在IE8和早期版本上使用,但KO本身的indexOf 能夠在全部瀏覽器上使用)

在數組操做函數方面(例如push和splice),KO本身的方式能夠自動觸發依賴跟蹤,而且通知全部的訂閱者它的變化,而後讓UI界面也相應的自動更新。

語法更方便,調用KO的push方法,只須要這樣寫:myObservableArray.push(...)。 好比原生數組的myObservableArray().push(...)好用多了。

 

下面講解的均是observableArray的讀取和寫入的相關函數。

indexOf

indexOf 函數返回的是第一個等於你參數數組項的索引。例如:myObservableArray.indexOf('Blah')將返回以0爲第一個索引的第一個等於Blah的數組項的索引。若是沒有找到相等的,將返回-1。

slice

slice函數是observableArray相對於JavaScript 原生函數slice的等價函數(返回給定的從開始索引到結束索引之間全部的對象集合)。 調用myObservableArray.slice(...)等價於調用JavaScript原生函數(例如:myObservableArray().slice(...))。

 

操做observableArray

observableArray 展示的是數組對象類似的函數並通知訂閱者的功能。

pop, push, shift, unshift, reverse, sort, splice

全部這些函數都是和JavaScript數組原生函數等價的,惟一不一樣的數組改變能夠通知訂閱者:

    myObservableArray.push('Some new value') 在數組末尾添加一個新項

    myObservableArray.pop() 刪除數組最後一個項並返回該項

    myObservableArray.unshift('Some new value') 在數組頭部添加一個項

    myObservableArray.shift() 刪除數組頭部第一項並返回該項

    myObservableArray.reverse() 翻轉整個數組的順序

    myObservableArray.sort() 給數組排序

        默認狀況下,是按照字符排序(若是是字符)或者數字排序(若是是數字)。

        你能夠排序傳入一個排序函數進行排序,該排序函數須要接受2個參數(表明該數組裏須要比較的項),若是第一個項小於第二個項,返回-1,大於則返回1,等於返回0。例如:用lastname給person排序,你能夠這樣寫:myObservableArray.sort (function (left, right) {return left.lastName == right.lastName? 0: (left.lastName < right.lastName? -1: 1) })

     myObservableArray.splice() 刪除指定開始索引和指定數目的數組對象元素。例如myObservableArray.splice(1, 3) 從索引1開始刪除3個元素(第2,3,4個元素)而後將這些元素做爲一個數組對象返回。

更多observableArray 函數的信息,請參考等價的JavaScript數組標準函數

 

remove和removeAll

observableArray 添加了一些JavaScript數組默認沒有但很是有用的函數:

    myObservableArray.remove(someItem) 刪除全部等於someItem的元素並將被刪除元素做爲一個數組返回

    myObservableArray.remove(function(item) { return item.age < 18 }) 刪除全部age屬性小於18的元素並將被刪除元素做爲一個數組返回

    myObservableArray.removeAll(['Chad', 132, undefined]) 刪除全部等於'Chad', 123, or undefined的元素並將被刪除元素做爲一個數組返回

 

destroy和destroyAll(注:一般只和和Ruby on Rails開發者有關)

destroy和destroyAll函數是爲Ruby on Rails開發者方便使用爲開發的:

    myObservableArray.destroy(someItem) 找出全部等於someItem的元素並給他們添加一個屬性_destroy,並賦值爲true

    myObservableArray.destroy(function(someItem) { return someItem.age < 18 }) 找出全部age屬性小於18的元素並給他們添加一個屬性_destroy,並賦值爲true

    myObservableArray.destroyAll(['Chad', 132, undefined]) 找出全部等於'Chad', 123, 或undefined 的元素並給他們添加一個屬性_destroy,並賦值爲true

 

那麼,_destroy是作什麼用的?正如我提到的,這只是爲Rails 開發者準備的。在Rails 開發過程當中,若是你傳入一個JSON對象,Rails 框架會自動轉換成ActiveRecord對象而且保存到數據庫。Rails 框架知道哪些對象以及在數據庫中存在,哪些須要添加或更新, 標記_destroy爲true就是告訴框架刪除這條記錄。

 

注意的是:在KO render一個foreach模板的時候,會自動隱藏帶有_destroy屬性而且值爲true的元素。因此若是你的「delete」按鈕調用destroy(someItem) 方法的話,UI界面上的相對應的元素將自動隱藏,而後等你提交這個JSON對象到Rails上的時候,這個元素項將從數據庫刪除(同時其它的元素項將正常的插入或者更新)。

Knockout應用開發指南 第三章:綁定語法(1)

2011-11-23 08:23 by 湯姆大叔, 20105 閱讀, 7 評論, 收藏, 編輯

第三章全部代碼都須要啓用KO的ko.applyBindings(viewModel);功能,才能使代碼生效,爲了節約篇幅,全部例子均省略了此行代碼。

1   visible 綁定

目的

visible綁定到DOM元素上,使得該元素的hidden或visible狀態取決於綁定的值。

 

例子

<div data-bind="visible: shouldShowMessage">
    You will see this message only when "shouldShowMessage" holds a true value.
</div>

<script type="text/javascript">
    var viewModel = {
        shouldShowMessage: ko.observable(true) // Message initially visible
    };
    viewModel.shouldShowMessage(false); // ... now it's hidden
    viewModel.shouldShowMessage(true); // ... now it's visible again
</script>

 

參數

    主參數

        當參數設置爲一個假值時(例如:布爾值false, 數字值0, 或者null, 或者undefined) ,該綁定將設置該元素的style.display值爲none,讓元素隱藏。它的優先級高於你在CSS裏定義的任何display樣式。

        當參數設置爲一個真值時(例如:布爾值true,或者非空non-null的對象或者數組) ,該綁定會刪除該元素的style.display值,讓元素可見。而後你在CSS裏自定義的display樣式將會自動生效。

        若是參數是監控屬性observable的,那元素的visible狀態將根據參數值的變化而變化,若是不是,那元素的visible狀態將只設置一次而且之後不在更新。

     其它參數

        無

 

注:使用函數或者表達式來控制元素的可見性

你也可使用JavaScript函數或者表達式做爲參數。這樣的話,函數或者表達式的結果將決定是否顯示/隱藏這個元素。例如:

<div data-bind="visible: myValues().length > 0">
    You will see this message only when 'myValues' has at least one member.
</div>

<script type="text/javascript">
    var viewModel = {
        myValues: ko.observableArray([]) // Initially empty, so message hidden
    };
    viewModel.myValues.push("some value"); // Now visible
</script>

 

依賴性

除KO核心類庫外,無依賴。

 

2   text 綁定

目的

text 綁定到DOM元素上,使得該元素顯示的文本值爲你綁定的參數。該綁定在顯示<span>或者<em>上很是有用,可是你能夠用在任何元素上。

 

例子

Today's message is: <span data-bind="text: myMessage"></span>

<script type="text/javascript">
    var viewModel = {
        myMessage: ko.observable() // Initially blank
    };
    viewModel.myMessage("Hello, world!"); // Text appears
</script>

 

參數

    主參數

    KO將參數值會設置在元素的innerText (IE)或textContent(Firefox和其它類似瀏覽器)屬性上。原來的文本將會被覆蓋。

    若是參數是監控屬性observable的,那元素的text文本將根據參數值的變化而更新,若是不是,那元素的text文本將只設置一次而且之後不在更新。

    若是你傳的是否是數字或者字符串(例如一個對象或者數組),那顯示的文本將是yourParameter.toString()的等價內容。

    其它參數

        無

 

1:使用函數或者表達式來決定text

若是你想讓你的text更可控,那選擇是建立一個依賴監控屬性(dependent observable),而後在它的執行函數裏編碼,決定應該顯示什麼樣的text文本。

例如:

The item is <span data-bind="text: priceRating"></span> today.


<script type="text/javascript">
    var viewModel = {
        price: ko.observable(24.95)
    };

    viewModel.priceRating = ko.dependentObservable(function () {
        returnthis.price() >50?"expensive" : "affordable";
    }, viewModel);
</script>

如今,text文本將在「expensive」和「affordable」之間替換,取決於價格怎麼改變。

然而,若是有相似需求的話其實沒有必要再聲明一個依賴監控屬性(dependent observable), 你只須要按照以下代碼寫JavaScript表達式就能夠了:

The item is <span data-bind="text: price() > 50 ? 'expensive' : 'affordable'"></span> today.

結果是同樣的,但咱們不須要再聲明依賴監控屬性(dependent observable)。

 

2:關於HTML encoding

由於該綁定是設置元素的innerText或textContent (而不是innerHTML),因此它是安全的,沒有HTML或者腳本注入的風險。例如:若是你編寫以下代碼:

viewModel.myMessage("<i>Hello, world!</i>");

… 它不會顯示斜體字,而是原樣輸出標籤。若是你須要顯示HTML內容,請參考html綁定.

 

3:關於IE 6的白空格whitespace

IE6有個奇怪的問題,若是 span裏有空格的話,它將自動變成一個空的span。若是你想編寫以下的代碼的話,那Knockout將不起任何做用:

Welcome, <span data-bind="text: userName"></span> to our web site.

… IE6 將不會顯示span中間的那個空格,你能夠經過下面這樣的代碼避免這個問題:

Welcome, <span data-bind="text: userName">&nbsp;</span> to our web site.

IE6之後版本和其它瀏覽器都沒有這個問題

 

依賴性

除KO核心類庫外,無依賴。

 

3   html 綁定

目的

html綁定到DOM元素上,使得該元素顯示的HTML值爲你綁定的參數。若是在你的view model裏聲明HTML標記而且render的話,那很是有用。

 

例子

<div data-bind="html: details"></div>

<script type="text/javascript">
    var viewModel = {
        details: ko.observable() // Initially blank
    };

    viewModel.details("<em>For further details, view the report <a href='report.html'>here</a>.</em>");
    // HTML content appears
</script> 

 

參數

    主參數

    KO設置該參數值到元素的innerHTML屬性上,元素以前的內容將被覆蓋。

    若是參數是監控屬性observable的,那元素的內容將根據參數值的變化而更新,若是不是,那元素的內容將只設置一次而且之後不在更新。

    若是你傳的是否是數字或者字符串(例如一個對象或者數組),那顯示的文本將是yourParameter.toString()的等價內容。

    其它參數

        無

 

注:關於HTML encoding

由於該綁定設置元素的innerHTML,你應該注意不要使用不安全的HTML代碼,由於有可能引發腳本注入攻擊。若是你不確信是否安全(好比顯示用戶輸入的內容),那你應該使用text綁定,由於這個綁定只是設置元素的text 值innerText和textContent。

 

依賴性

除KO核心類庫外,無依賴。

 

4   css 綁定

目的

css綁定是添加或刪除一個或多個CSS class到DOM元素上。 很是有用,好比當數字變成負數時高亮顯示。(注:若是你不想應用CSS class而是想引用style屬性的話,請參考style綁定。)

 

例子

<div data-bind="css: { profitWarning: currentProfit() < 0 }">
   Profit Information
</div>
 

<script type="text/javascript">
    var viewModel = {
        currentProfit: ko.observable(150000)
        // Positive value, so initially we don't apply the "profitWarning" class
    };

    viewModel.currentProfit(-50);
    // Causes the "profitWarning" class to be applied
</script>

效果就是當currentProfit 小於0的時候,添加profitWarning CSS class到元素上,若是大於0則刪除這個CSS class。

 

參數

    主參數

    該參數是一個JavaScript對象,屬性是你的CSS class名稱,值是比較用的true或false,用來決定是否應該使用這個CSS class。

    你能夠一次設置多個CSS class。例如,若是你的view model有一個叫isServre的屬性,

<div data-bind="css: { profitWarning: currentProfit() < 0, majorHighlight: isSevere }">

    非布爾值會被解析成布爾值。例如, 0和null被解析成false,21和非null對象被解析成true。

    若是參數是監控屬性observable的,那隨着值的變化將會自動添加或者刪除該元素上的CSS class。若是不是,那CSS class將會只添加或者刪除一次而且之後不在更新。

    你可使用任何JavaScript表達式或函數做爲參數。KO將用它的執行結果來決定是否應用或刪除CSS class。

    其它參數

        無

 

注:應用的CSS class的名字不是合法的JavaScript變量命名

若是你想使用my-class class,你不能寫成這樣:

<div data-bind="css: { my-class: someValue }">...</div>

… 由於my-class不是一個合法的命名。解決方案是:在my-class兩邊加引號做爲一個字符串使用。這是一個合法的JavaScript 對象 文字(從JSON技術規格說明來講,你任什麼時候候都應該這樣使用,雖然不是必須的)。例如,

<div data-bind="css: { 'my-class': someValue }">...</div>

 

依賴性

除KO核心類庫外,無依賴。

 

5   style 綁定

目的

style綁定是添加或刪除一個或多個DOM元素上的style值。好比當數字變成負數時高亮顯示,或者根據數字顯示對應寬度的Bar。(注:若是你不是應用style值而是應用CSS class的話,請參考CSS綁定。)

 

例子

<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black' }">
   Profit Information
</div>


<script type="text/javascript">
    var viewModel = {
        currentProfit: ko.observable(150000) // Positive value, so initially black
    };
    viewModel.currentProfit(-50); // Causes the DIV's contents to go red
</script>

當currentProfit 小於0的時候div的style.color是紅色,大於的話是黑色。

 

參數

    主參數

    該參數是一個JavaScript對象,屬性是你的style的名稱,值是該style須要應用的值。

    你能夠一次設置多個style值。例如,若是你的view model有一個叫isServre的屬性,

<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black', fontWeight: isSevere() ? 'bold' : '' }">...</div>

    若是參數是監控屬性observable的,那隨着值的變化將會自動添加或者刪除該元素上的style值。若是不是,那style值將會只應用一次而且之後不在更新。

    你可使用任何JavaScript表達式或函數做爲參數。KO將用它的執行結果來決定是否應用或刪除style值。

    其它參數

        無

 

注:應用的style的名字不是合法的JavaScript變量命名

若是你須要應用font-weight或者text-decoration,你不能直接使用,而是要使用style對應的JavaScript名稱。

    錯誤: { font-weight: someValue };            正確: { fontWeight: someValue }

    錯誤: { text-decoration: someValue };      正確: { textDecoration: someValue }

參考:style名稱和對應的JavaScript 名稱列表

 

依賴性

除KO核心類庫外,無依賴。

 

6   attr 綁定

目的

attr 綁定提供了一種方式能夠設置DOM元素的任何屬性值。你能夠設置img的src屬性,鏈接的href屬性。使用綁定,當模型屬性改變的時候,它會自動更新。

 

例子

<a data-bind="attr: { href: url, title: details }">
    Report
</a>

<script type="text/javascript">
    var viewModel = {
        url: ko.observable("year-end.html"),
        details: ko.observable("Report including final year-end statistics")
    };
</script>

呈現結果是該鏈接的href屬性被設置爲year-end.html, title屬性被設置爲Report including final year-end statistics。

 

參數

    主參數

    該參數是一個JavaScript對象,屬性是你的attribute名稱,值是該attribute須要應用的值。

    若是參數是監控屬性observable的,那隨着值的變化將會自動添加或者刪除該元素上的attribute值。若是不是,那attribute值將會只應用一次而且之後不在更新。

    其它參數

        無

 

注:應用的屬性名字不是合法的JavaScript變量命名

若是你要用的屬性名稱是data-something的話,你不能這樣寫:

<div data-bind="attr: { data-something: someValue }">...</div>

… 由於data-something 不是一個合法的命名。解決方案是:在data-something兩邊加引號做爲一個字符串使用。這是一個合法的JavaScript 對象 文字(從JSON技術規格說明來講,你任什麼時候候都應該這樣使用,雖然不是必須的)。例如,

<div data-bind="attr: { ‘data-something’: someValue }">...</div>

 

依賴性

除KO核心類庫外,無依賴。

Knockout應用開發指南 第三章:綁定語法(2)

2011-11-24 09:00 by 湯姆大叔, 16408 閱讀, 22 評論, 收藏, 編輯

7   click 綁定

目的

click綁定在DOM元素上添加事件句柄以便元素被點擊的時候執行定義的JavaScript 函數。大部分是用在button,input和鏈接a上,可是能夠在任意元素上使用。

 

例子

<div>
    You've clicked <span data-bind="text: numberOfClicks"></span> times
    <button data-bind="click: incrementClickCounter">Click me</button>
</div>


<script type="text/javascript">
    var viewModel = {
        numberOfClicks: ko.observable(0),
        incrementClickCounter: function () {
            var previousCount =this.numberOfClicks();
            this.numberOfClicks(previousCount +1);
        }
    };
</script>

每次點擊按鈕的時候,都會調用incrementClickCounter()函數,而後更新自動更新點擊次數。

 

參數

    主參數

    Click點擊事件時所執行的函數。

    你能夠聲明任何JavaScript函數 – 不必定非要是view model裏的函數。你能夠聲明任意對象上的任何函數,例如: someObject.someFunction。

    View model上的函數在用的時候有一點點特殊,就是不須要引用對象的,直接引用函數自己就好了,好比直接寫incrementClickCounter 就能夠了,而無需寫成: viewModel.incrementClickCounter(儘管是合法的)。

    其它參數

        無

 

注1:傳參數給你的click 句柄

最簡單的辦法是傳一個function包裝的匿名函數:

<button data-bind="click: function() { viewModel.myFunction('param1', 'param2') }">
    Click me
</button>

這樣,KO就會調用這個匿名函數,裏面會執行viewModel.myFunction(),而且傳進了'param1' 和'param2'參數。

 

注2:訪問事件源對象

有些狀況,你可能須要使用事件源對象,Knockout會將這個對象傳遞到你函數的第一個參數:

<button data-bind="click: myFunction">
    Click me
</button>


 <script type="text/javascript">
     var viewModel = {
         myFunction: function (event) {
             if (event.shiftKey) {
                 //do something different when user has shift key down
             } else {
                 //do normal action
             }
         }
     };
</script>

若是你須要的話,可使用匿名函數的第一個參數傳進去,而後在裏面調用:

<button data-bind="click: function(event) { viewModel.myFunction(event, 'param1', 'param2') }">
    Click me
</button>

這樣,KO就會將事件源對象傳遞給你的函數而且使用了。

 

注3: 容許執行默認事件

默認狀況下,Knockout會阻止冒泡,防止默認的事件繼續執行。例如,若是你點擊一個a鏈接,在執行完自定義事件時它不會鏈接到href地址。這特別有用是由於你的自定義事件主要就是操做你的view model,而不是鏈接到另一個頁面。

固然,若是你想讓默認的事件繼續執行,你能夠在你click的自定義函數裏返回true。

 

注4:控制this句柄

初學者能夠忽略這小節,由於大部分都用不着,高級用戶能夠參考以下內容:

KO在調用你定義的函數時,會將view model傳給this對象(也就是ko.applyBindings使用的view model)。主要是方便你在調用你在view model裏定義的方法的時候能夠很容易再調用view model裏定義的其它屬性。例如: this.someOtherViewModelProperty。

若是你想引用其它對象,咱們有兩種方式:

你能夠和注1裏那樣使用匿名函數,由於它支持任意JavaScript 對象。

 

你也能夠直接引用任何函數對象。你可使用bind使callback函數設置this爲任何你選擇的對象。例如:

<button data-bind="click: someObject.someFunction.bind(someObject)">
    Click me
</button>

 

若是你是C#或Java開發人員,你能夠疑惑爲何咱們還要用bind函數到一個對象想,特別是像調用someObject.someFunction。 緣由是在JavaScript裏,函數本身不是類的一部分,他們在單獨存在的對象,有可能多個對象都引用一樣的someFunction函數,因此當這個函數被調用的時候它不知道誰調用的(設置this給誰)。在你bind以前運行時是不會知道的。KO默認狀況下設置this對象是view model,但你能夠用bind語法重定義它。

 

在注1裏使用匿名函數的時候沒有具體的要求,由於JavaScript代碼 someObject.someFunction()就意味着調用someFunction,而後設置this到 someObject對象上。

 

注5:防止事件冒泡

默認狀況下,Knockout容許click事件繼續在更高一層的事件句柄上冒泡執行。例如,若是你的元素和父元素都綁定了click事件,那當你點擊該元素的時候兩個事件都會觸發的。若是須要,你能夠經過額外的綁定clickBubble來禁止冒泡。例如:

<div data-bind="click: myDivHandler">
    <button data-bind="click: myButtonHandler, clickBubble: false">
        Click me
    </button>
</div>

默認狀況下,myButtonHandler會先執行,而後會冒泡執行myDivHandler。但一旦你設置了clickBubble爲false的時候,冒泡事件會被禁止。

 

依賴性

除KO核心類庫外,無依賴。

 

8   event 綁定

目的

event綁定在DOM元素上添加指定的事件句柄以便元素被觸發的時候執行定義的JavaScript 函數。大部分狀況下是用在keypress,mouseover和mouseout上。

 

例子

<div>
    <div data-bind="event: { mouseover: enableDetails, mouseout: disableDetails }">
        Mouse over me
    </div>
    <div data-bind="visible: detailsEnabled">
        Details
    </div>
</div>


<script type="text/javascript">
    var viewModel = {
        detailsEnabled: ko.observable(false),
        enableDetails: function () {
            this.detailsEnabled(true);
        },
        disableDetails: function () {
            this.detailsEnabled(false);
        }
    };
</script>

每次鼠標在第一個元素上移入移出的時候都會調用view model上的方法來toggle detailsEnabled的值,而第二個元素會根據detailsEnabled的值自動顯示或者隱藏。

 

參數

    主參數

    你須要傳入的是一個JavaScript對象,他的屬性名是事件名稱,值是你所須要執行的函數。

    你能夠聲明任何JavaScript函數 – 不必定非要是view model裏的函數。你能夠聲明任意對象上的任何函數,例如: event: { mouseover: someObject.someFunction }。

    View model上的函數在用的時候有一點點特殊,就是不須要引用對象的,直接引用函數自己就好了,好比直接寫event: { mouseover: enableDetails } 就能夠了,而無需寫成: event: { mouseover: viewModel.enableDetails }(儘管是合法的)。

    其它參數

        無

 

注1:傳參數給你的click 句柄

最簡單的辦法是傳一個function包裝的匿名函數:

<button data-bind="event: { mouseover: function() { viewModel.myFunction('param1', 'param2') } }">
    Click me
</button>

這樣,KO就會調用這個匿名函數,裏面會執行viewModel.myFunction(),而且傳進了'param1' 和'param2'參數。

 

注2:訪問事件源對象

有些狀況,你可能須要使用事件源對象,Knockout會將這個對象傳遞到你函數的第一個參數:

<div data-bind="event: { mouseover: myFunction }">
    Mouse over me
</div>

 
 <script type="text/javascript">
     var viewModel = {
         myFunction: function (event) {
             if (event.shiftKey) {
                 //do something different when user has shift key down
             } else {
                 //do normal action
             }
         }
     };
</script>

若是你須要的話,可使用匿名函數的第一個參數傳進去,而後在裏面調用:

<div data-bind="event: { mouseover: function(event) { viewModel.myFunction(event, 'param1', 'param2') } }">
    Mouse over me
</div>

這樣,KO就會將事件源對象傳遞給你的函數而且使用了。

 

注3: 容許執行默認事件

默認狀況下,Knockout會阻止冒泡,防止默認的事件繼續執行。例如,若是在一個input標籤上綁定一個keypress事件,當你輸入內容的時候,瀏覽器只會調用你的函數而不是天價你輸入的值。另一個例子click綁定,當你點擊一個a鏈接,在執行完自定義事件時它不會鏈接到href地址。由於你的自定義事件主要就是操做你的view model,而不是鏈接到另一個頁面。

固然,若是你想讓默認的事件繼續執行,你能夠在你event的自定義函數裏返回true。

 

注4:控制this句柄

初學者能夠忽略這小節,由於大部分都用不着,高級用戶能夠參考以下內容:

KO在調用你定義的event綁定函數時,會將view model傳給this對象(也就是ko.applyBindings使用的view model)。主要是方便你在調用你在view model裏定義的方法的時候能夠很容易再調用view model裏定義的其它屬性。例如: this.someOtherViewModelProperty。

 

若是你想引用其它對象,咱們有兩種方式:

你能夠和注1裏那樣使用匿名函數,由於它支持任意JavaScript 對象。

你也能夠直接引用任何函數對象。你可使用bind使callback函數設置this爲任何你選擇的對象。例如:

<div data-bind="event: { mouseover: someObject.someFunction.bind(someObject) }">
    Mouse over me
</div>

若是你是C#或Java開發人員,你能夠疑惑爲何咱們還要用bind函數到一個對象想,特別是像調用someObject.someFunction。 緣由是在JavaScript裏,函數本身不是類的一部分,他們在單獨存在的對象,有可能多個對象都引用一樣的someFunction函數,因此當這個函數被調用的時候它不知道誰調用的(設置this給誰)。在你bind以前運行時是不會知道的。KO默認狀況下設置this對象是view model,但你能夠用bind語法重定義它。

 

在注1裏使用匿名函數的時候沒有具體的要求,由於JavaScript代碼 someObject.someFunction()就意味着調用someFunction,而後設置this到 someObject對象上。

 

注5:防止事件冒泡

默認狀況下,Knockout容許event事件繼續在更高一層的事件句柄上冒泡執行。例如,若是你的元素和父元素都綁定了mouseover事件,那麼若是你的鼠標在該元素移動的時候兩個事件都會觸發的。若是須要,你能夠經過額外的綁定youreventBubble來禁止冒泡。例如:

<div data-bind="event: { mouseover: myDivHandler }">
    <button data-bind="event: { mouseover: myButtonHandler }, mouseoverBubble: false">
        Click me
    </button>
</div>

默認狀況下,myButtonHandler會先執行,而後會冒泡執行myDivHandler。但一旦你設置了mouseoverBubble爲false的時候,冒泡事件會被禁止。

 

依賴性

除KO核心類庫外,無依賴。

 

9   submit 綁定

目的

submit綁定在form表單上添加指定的事件句柄以便該form被提交的時候執行定義的JavaScript 函數。只能用在表單form元素上。

當你使用submit綁定的時候, Knockout會阻止form表單默認的submit動做。換句話說,瀏覽器會執行你定義的綁定函數而不會提交這個form表單到服務器上。能夠很好地解釋這個,使用submit綁定就是爲了處理view model的自定義函數的,而不是再使用普通的HTML form表單。若是你要繼續執行默認的HTML form表單操做,你能夠在你的submit句柄裏返回true。

 

例子

<form data-bind="submit: doSomething">
    ... form contents go here ...
    <button type="submit">Submit</button>
</div>

<script type="text/javascript">
    var viewModel = {
        doSomething: function (formElement) {
            // ... now do something
        }
    };
</script>

這個例子裏,KO將把整個form表單元素做爲參數傳遞到你的submit綁定函數裏。 你能夠忽略無論,可是有些例子裏是否有用,參考:ko.postJson工具。

 

爲何不在submit按鈕上使用click綁定?

在form上,你可使用click綁定代替submit綁定。不過submit能夠handle其它的submit行爲,好比在輸入框裏輸入回車的時候能夠提交表單。

 

參數

    主參數

    你綁定到submit事件上的函數

    你能夠聲明任何JavaScript函數 – 不必定非要是view model裏的函數。你能夠聲明任意對象上的任何函數,例如: submit: someObject.someFunction。

    View model上的函數在用的時候有一點點特殊,就是不須要引用對象的,直接引用函數自己就好了,好比直接寫submit: doSomething就能夠了,而無需寫成: submit: viewModel. doSomething(儘管是合法的)。

    其它參數

        無

 

備註:

關於若是傳遞更多的參數給submit綁定函數,或者當調用非view model裏的函數的時如何控制this,請參考click綁定。全部click綁定相關的notes也都適用於submit綁定。

 

依賴性

除KO核心類庫外,無依賴。

 

10   enable 綁定

目的

enable綁定使DOM元素只有在參數值爲 true的時候才enabled。在form表單元素input,select,和textarea上很是有用。

 

例子

<p>
    <input type='checkbox' data-bind="checked: hasCellphone"/>
    I have a cellphone
</p>

<p>
    Your cellphone number:
    <input type='text' data-bind="value: cellphoneNumber, enable: hasCellphone"/>
</p>
 
<script type="text/javascript">
    var viewModel = {
        hasCellphone: ko.observable(false),
        cellphoneNumber: ""
    };
</script>

這個例子裏,「Your cellphone number」後的text box 初始狀況下是禁用的,只有當用戶點擊標籤 「I have a cellphone」的時候纔可用。

 

參數

    主參數

    聲明DOM元素是否可用enabled。

    非布爾值會被解析成布爾值。例如0和null被解析成false,21和非null對象被解析給true。

    若是你的參數是observable的,那綁定會隨着observable值的改變而自動更新enabled/disabled狀態。若是不是,則只會設置一次而且之後再也不更新。

    其它參數

         無

 

注:任意使用JavaScript表達式

不牢牢限制於變量 – 你可使用任何JavaScript表達式來控制元素是否可用。例如,

<button data-bind="enabled: parseAreaCode(viewModel.cellphoneNumber()) != '555'">
    Do something
</button>

 

依賴性

除KO核心類庫外,無依賴。

 

11   disable 綁定

目的

disable綁定使DOM元素只有在參數值爲 true的時候才disabled。在form表單元素input,select,和textarea上很是有用。

disable綁定和enable綁定正好相反,詳情請參考enable綁定。

Knockout應用開發指南 第三章:綁定語法(3)

2011-11-24 12:52 by 湯姆大叔, 18882 閱讀, 9 評論, 收藏, 編輯

12   value 綁定

目的

value綁定是關聯DOM元素的值到view model的屬性上。主要是用在表單控件<input>,<select>和<textarea>上。

當用戶編輯表單控件的時候, view model對應的屬性值會自動更新。一樣,當你更新view model屬性的時候,相對應的元素值在頁面上也會自動更新。

注:若是你在checkbox或者radio button上使用checked綁定來讀取或者寫入元素的 checked狀態,而不是value 值的綁定。

 

例子

<p>Login name: <input data-bind="value: userName"/></p>
<p>Password: <input type="password" data-bind="value: userPassword"/></p>
 

<script type="text/javascript">
    var viewModel = {
        userName: ko.observable(""),        // Initially blank
        userPassword: ko.observable("abc"), // Prepopulate
    };
</script>

 

參數

    主參數

    KO設置此參數爲元素的value值。以前的值將被覆蓋。

    若是參數是監控屬性observable的,那元素的value值將根據參數值的變化而更新,若是不是,那元素的value值將只設置一次而且之後不在更新。

    若是你提供的參數不是一個數字或者字符串(而是對象或者數組)的話,那顯示的value值就是yourParameter.toString() 的內容(一般沒用,因此最好都設置爲數字或者字符串)。

    無論何時,只要你更新了元素的值,那 KO都會將view model對應的屬性值自動更新。默認狀況下當用戶離開焦點(例如onchange事件)的時候,KO才更新這個值,可是你能夠經過第2個參數valueUpdate來特別指定改變值的時機。

 

    其它參數

        valueUpdate

        若是你使用valueUpdate參數,那就是意味着KO將使用自定義的事件而不是默認的離開焦點事件。下面是一些最經常使用的選項:

            「change」(默認值) - 當失去焦點的時候更新view model的值,或者是<select> 元素被選擇的時候。

            「keyup」 – 當用戶敲完一個字符之後當即更新view model。

            「keypress」 – 當用戶正在敲一個字符但沒有釋放鍵盤的時候就當即更新view model。不像 keyup,這個更新和keydown是同樣的。

            「afterkeydown」 – 當用戶開始輸入字符的時候就更新view model。主要是捕獲瀏覽器的keydown事件或異步handle事件。

        上述這些選項,若是你想讓你的view model進行實時更新,使用「afterkeydown」是最好的選擇。

 

例子:

<p>Your value: <input data-bind="value: someValue, valueUpdate: 'afterkeydown'"/></p>
<p>You have typed: <span data-bind="text: someValue"></span></p> <!-- updates in real-time -->

<script type="text/javascript">
    var viewModel = {
        someValue: ko.observable("edit me")
    };
</script>

 

注1:綁定下拉菜單drop-down list(例如SELECT)

Knockout對下拉菜單drop-down list綁定有一個特殊的支持,那就是在讀取和寫入綁定的時候,這個值能夠是任意JavaScript對象,而沒必要非得是字符串。在你讓你用戶選擇一組model對象的時候很是有用。具體例子,參考options綁定。

相似,若是你想建立一個multi-select list,參考selectedOptions綁定。

 

注2:更新observable和non-observable屬性值

若是你用value綁定將你的表單元素和你的observable屬性關聯起來,KO設置的2-way的雙向綁定,任何一方改變都會更新另一方的值。

可是,若是你的元素綁定的是一個non-observable屬性(例如是一個原始的字符串或者JavaScript表達式) ,KO會這樣執行:

若是你綁定的non-observable屬性是簡單對象,例如一個常見的屬性值,KO會設置這個值爲form表單元素的初始值,若是你改變form表單元素的值,KO會將值從新寫回到view mode的這個屬性。但當這個屬性本身改變的時候,元素卻不會再變化了(由於不是observable的),因此它僅僅是1-way綁定。

若是你綁定的non-observable屬性是複雜對象,例如複雜的JavaScript 表達式或者子屬性,KO也會設置這個值爲form表單元素的初始值,可是改變form表單元素的值的時候,KO不會再寫會view model屬性,這種狀況叫one-time-only value setter,不是真正的綁定。

 

例子:

<p>First value: <input data-bind="value: firstValue"/></p>          <!-- two-way binding -->
<p>Second value: <input data-bind="value: secondValue"/></p>        <!-- one-way binding -->
<p>Third value: <input data-bind="value: secondValue.length"/></p>  <!-- no binding -->

<script type="text/javascript">
    var viewModel = {
        firstValue: ko.observable("hello"), // Observable
        secondValue: "hello, again"// Not observable
    };
    ko.applyBindings(viewModel);
</script>

 

依賴性

除KO核心類庫外,無依賴。

 

13   checked 綁定

目的

checked綁定是關聯到checkable的form表單控件到view model上 - 例如checkbox(<input type='checkbox'>)或者radio button(<input type='radio'>) 。當用戶check關聯的form表單控件的時候,view model對應的值也會自動更新,相反,若是view model的值改變了,那控件元素的check/uncheck狀態也會跟着改變。

注:對text box,drop-down list和全部non-checkable的form表單控件,用value綁定來讀取和寫入是該元素的值,而不是checked綁定。

例子

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam"/></p>

<script type="text/javascript">
    var viewModel = {
        wantsSpam: ko.observable(true) // Initially checked
    };

     // ... then later ...
    viewModel.wantsSpam(false); // The checkbox becomes unchecked
</script>

 

Checkbox關聯到數組

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam"/></p>
<div data-bind="visible: wantsSpam">
    Preferred flavors of spam:
    <div><input type="checkbox" value="cherry" data-bind="checked: spamFlavors"/> Cherry</div>
    <div><input type="checkbox" value="almond" data-bind="checked: spamFlavors"/> Almond</div>
    <div><input type="checkbox" value="msg" data-bind="checked: spamFlavors"/> Monosodium Glutamate</div>
</div>

 
<script type="text/javascript">

    var viewModel = {
        wantsSpam: ko.observable(true),
        spamFlavors: ko.observableArray(["cherry", "almond"]) // Initially checks the Cherry and Almond checkboxes
    };

    // ... then later ...
    viewModel.spamFlavors.push("msg"); // Now additionally checks the Monosodium Glutamate checkbox
</script>

 

添加radio button

<p>Send me spam: <input type="checkbox" data-bind="checked: wantsSpam"/></p>

<div data-bind="visible: wantsSpam">
    Preferred flavor of spam:
    <div><input type="radio" name="flavorGroup" value="cherry" data-bind="checked: spamFlavor"/> Cherry</div>
    <div><input type="radio" name="flavorGroup" value="almond" data-bind="checked: spamFlavor"/> Almond</div>
    <div><input type="radio" name="flavorGroup" value="msg" data-bind="checked: spamFlavor"/> Monosodium Glutamate</div>
</div>

 
<script type="text/javascript">

    var viewModel = {
        wantsSpam: ko.observable(true),
        spamFlavor: ko.observable("almond") // Initially selects only the Almond radio button
    };

     // ... then later ...
    viewModel.spamFlavor("msg"); // Now only Monosodium Glutamate is checked
</script>

 

參數

    主參數

    KO會設置元素的checked狀態匹配到你的參數上,以前的值將被覆蓋。對參數的解析取決於你元素的類型:

        對於checkbox,當參數爲true的時候,KO會設置元素的狀態爲checked,反正設置爲unchecked。若是你傳的參數不是布爾值,那KO將會解析成布爾值。也就是說非0值和非null對象,非空字符串將被解析成true,其它值都被解析成false。

        當用戶check或者uncheck這個checkbox的時候,KO會將view model的屬性值相應地設置爲true或者false。

        一個特殊狀況是參數是一個數組,若是元素的值存在於數組,KO就會將元素設置爲checked,若是數組裏不存在,就設置爲unchecked。若是用戶對checkbox進行check或uncheck,KO就會將元素的值添加數組或者從數組裏刪除。

        對於radio buttons,KO只有當參數值等於radio button value屬性值的時候才設置元素爲checked狀態。因此參數應是字符串。在上面的例子裏只有當view model 的spamFlavor 屬性等於「almond」的時候,該radio button纔會設置爲checked。

        當用戶將一個radio button選擇上的時候 is selected,KO會將該元素的value屬性值更新到view model屬性裏。上面的例子,當點擊value= 「cherry」的選項上, viewModel.spamFlavor的值將被設置爲「cherry」。

        固然,最有用的是設置一組radio button元素對應到一個單個的view model 屬性。確保一次只能選擇一個radio button須要將他們的name屬性名都設置成同樣的值(例如上個例子的flavorGroup值)。這樣的話,一次就只能選擇一個了。

    若是參數是監控屬性observable的,那元素的checked狀態將根據參數值的變化而更新,若是不是,那元素的value值將只設置一次而且之後不在更新。

    其它參數

    無

 

依賴性

除KO核心類庫外,無依賴。

 

14   options 綁定

目的

options綁定控制什麼樣的options在drop-down列表裏(例如:<select>)或者 multi-select 列表裏 (例如:<select size='6'>)顯示。此綁定不能用於<select>以外的元素。關聯的數據應是數組(或者是observable數組),<select>會遍歷顯示數組裏的全部的項。

 

注:對於multi-select列表,設置或者獲取選擇的多項須要使用selectedOptions綁定。對於single-select列表,你也可使用value綁定讀取或者設置元素的selected項。

 

例1:Drop-down list

<p>Destination country: <select data-bind="options: availableCountries"></select></p>

 
<script type="text/javascript">
    var viewModel = {
        availableCountries: ko.observableArray(['France', 'Germany', 'Spain']) // These are the initial options
    };

    // ... then later ...
    viewModel.availableCountries.push('China'); // Adds another option
</script>

 

例2:Multi-select list

<p>Choose some countries you'd like to visit: <select data-bind="options: availableCountries" size="5" multiple="true"></select></p>
 
<script type="text/javascript">
    var viewModel = {
        availableCountries: ko.observableArray(['France', 'Germany', 'Spain'])
    };
</script>

 

例3:Drop-down list展現的任意JavaScript對象,不只僅是字符串

<p>
    Your country:
    <select data-bind="options: availableCountries,
              optionsText: 'countryName', value: selectedCountry, optionsCaption: 'Choose...'"></select>
</p>

<div data-bind="visible: selectedCountry"> <!-- Appears when you select something -->
    You have chosen a country with population
    <span data-bind="text: selectedCountry() ? selectedCountry().countryPopulation : 'unknown'"></span>.
</div>

<script type="text/javascript">
    // Constructor for an object with two properties
var country =function (name, population) {
        this.countryName = name;
        this.countryPopulation = population;
    };

     var viewModel = {
        availableCountries: ko.observableArray([
            new country("UK", 65000000),
            new country("USA", 320000000),
            new country("Sweden", 29000000)
        ]),
        selectedCountry: ko.observable() // Nothing selected by default
    };
</script>

 

例4:Drop-down list展現的任意JavaScript對象,顯示text是function的返回值

<!-- Same as example 3, except the <select> box expressed as follows: -->

<select data-bind="options: availableCountries,
                   optionsText: function(item) {
                       return item.countryName + ' (pop: ' + item.countryPopulation + ')'
                   },
                   value: selectedCountry,
                   optionsCaption: 'Choose...'"></select>

 

注意例3和例4在optionsText值定義上的不一樣。

 

參數

    主參數

    該參數是一個數組(或者observable數組)。對每一個item,KO都會將它做爲一個<option> 添加到<select>裏,以前的options都將被刪除。

    若是參數是一個string數組,那你不須要再聲明任何其它參數。<select>元素會將每一個string顯示爲一個option。不過,若是你讓用戶選擇的是一個JavaScript對象數組(不只僅是string),那就須要設置optionsText和optionsValue這兩個參數了。

    若是參數是監控屬性observable的,那元素的options項將根據參數值的變化而更新,若是不是,那元素的value值將只設置一次而且之後不在更新。

 

    其它參數

        optionsCaption

        有時候,默認狀況下不想選擇任何option項。可是single-select drop-down列表因爲每次都要默認選擇以項目,怎麼避免這個問題呢?經常使用的方案是加一個「請選擇的」或者「Select an item」的提示語,或者其它相似的,而後讓這個項做爲默認選項。

        咱們使用optionsCaption參數就能很容易實現,它的值是字符串型,做爲默認項顯示。例如:

        <select data-bind='options: myOptions, optionsCaption: "Select an item...", value: myChosenValue'></select>

        KO會在全部選項上加上這一個項,而且設置value值爲undefined。因此,若是myChosenValue被設置爲undefined(默認是observable的),那麼上述的第一個項就會被選中。

 

        optionsText

        上面的例3展現的綁定JavaScript對象到option上 – 不只僅是字符串。這時候你須要設置這個對象的那個屬性做爲drop-down列表或multi-select列表的text來顯示。例如,例3中使用的是設置額外的參數optionsText將對象的屬性名countryName做爲顯示的文本。

        若是不想僅僅顯示對象的屬性值做爲每一個item項的text值,那你能夠設置optionsText 爲JavaScript 函數,而後再函數裏經過本身的邏輯返回相應的值(該函數參數爲item項自己)。例4展現的就是返回item的2個屬性值合併的結果。

 

        optionsValue

        和optionsText相似, 你也能夠經過額外參數optionsValue來聲明對象的那個屬性值做爲該<option>的value值。

        經典場景:如在更新options的時候想保留原來的已經選擇的項。例如,當你重複屢次調用Ajax獲取car列表的時候,你要確保已經選擇的某個car一直都是被選擇上,那你就須要設置optionsValue爲「carId」或者其它的unique標示符,不然的話KO找不知道以前選擇的car是新options裏的哪一項。

 

        selectedOptions

        對於multi-select列表,你能夠用selectedOptions讀取和設置多個選擇項。技術上看它是一個單獨的綁定,有本身的文檔,請參考: selectedOptions綁定。

 

注:已經被選擇的項會再options改變的時候保留

當使用options綁定<select>元素的時候,若是options改變,KO將盡量第保留以前已經被選擇的項不變(除非是你事先手工刪除一個或多個已經選擇的項)。這是由於options 綁定嘗試依賴value值的綁定(single-select列表)和selectedOptions綁定(multi-select列表)。

 

依賴性

除KO核心類庫外,無依賴。

 

15   selectedOptions 綁定

目的

selectedOptions綁定用於控制multi-select列表已經被選擇的元素,用在使用options綁定的<select>元素上。

當用戶在multi-select列表選擇或反選一個項的時候,會將view model的數組進行相應的添加或者刪除。一樣,若是view model上的這個數組是observable數組的話,你添加或者刪除任何item(經過push或者splice)的時候,相應的UI界面裏的option項也會被選擇上或者反選。這種方式是2-way綁定。

注:控制single-select下拉菜單選擇項,你可使用value綁定。

 

例子

<p>
    Choose some countries you'd like to visit:
    <select data-bind="options: availableCountries, selectedOptions: chosenCountries" size="5" multiple="true"></select>
</p>

<script type="text/javascript">
    var viewModel = {
        availableCountries: ko.observableArray(['France', 'Germany', 'Spain']),
        chosenCountries: ko.observableArray(['Germany']) // Initially, only Germany is selected
    };

    // ... then later ...
    viewModel.chosenCountries.push('France'); // Now France is selected too
</script>

 

參數

    主參數

    該參數是數組(或observable數組)。KO設置元素的已選項爲和數組裏match的項,以前的已選擇項將被覆蓋。

    若是參數是依賴監控屬性observable數組,那元素的已選擇項selected options項將根據參數值的變化(經過push,pop,或其它observable數組方法)而更新,若是不是,那元素的已選擇項selected options將只設置一次而且之後不在更新。

    無論該參數是否是observable數組,用戶在multi-select列表裏選擇或者反選的時候,KO都會探測到,而且更新數組裏的對象以達到同步的結果。這樣你就能夠獲取options已選項。

    其它參數

           無

 

注:支持讓用戶選擇任意JavaScript對象

在上面的例子裏,用戶能夠選擇數組裏的字符串值,可是選擇不限於字符串,若是你願意你能夠聲明包含任意JavaScript對象的數組,查看options綁定如何顯示JavaScript對象到列表裏。

這種場景,你能夠用selectedOptions來讀取或設置這些對象自己,而不是頁面上顯示的option表示形式,這樣作在大部分狀況下都很是清晰。view model就能夠探測到你從數組對象裏選擇的項了,而沒必要關注每一個項和頁面上展現的option項是如何map的。

 

依賴性

除KO核心類庫外,無依賴。

 

16   uniqueName 綁定

目的

uniqueName綁定確保所綁定的元素有一個非空的name屬性。若是該元素沒有name屬性,那綁定會給它設置一個unique的字符串值做爲name屬性。你不會常常用到它,只有在某些特殊的場景下才用到,例如:

在使用KO的時候,一些技術可能依賴於某些元素的name屬性,儘快他們沒有什麼意義。例如,jQuery Validation驗證當前只驗證有name屬性的元素。爲配合Knockout UI使用,有些時候須要使用uniqueName綁定避免讓jQuery Validation驗證出錯。

IE 6下,若是radio button沒有name屬性是不容許被checked了。大部分時候都沒問題,由於大部分時候radio button元素都會有name屬性的做爲一組互相的group。不過,若是你沒聲明,KO內部會在這些元素上使用uniqueName那麼以確保他們能夠被checked。

 

例子

<input data-bind="value: someModelProperty, uniqueName: true"/>

 

參數

    主參數

    就像上面的例子同樣,傳入true(或者能夠轉成true的值)以啓用uniqueName綁定。

    其它參數

        無

 

依賴性

除KO核心類庫外,無依賴。

Knockout應用開發指南 第四章:模板綁定

2011-11-25 09:01 by 湯姆大叔, 14468 閱讀, 17 評論, 收藏, 編輯

模板綁定The template binding

目的

template綁定經過模板將數據render到頁面。模板綁定對於構建嵌套結構的頁面很是方便。默認狀況, Knockout用的是流行的jquery.tmpl模板引擎。使用它的話,須要在安裝頁面下載和引用jquery.tmpl和jQuery框架。或者你 也能夠集成其它的模板引擎(雖然須要瞭解Knockout 內部知識才行)。

 

例子

<div data-bind='template: "personTemplate"'> </div>
<script id='personTemplate' type='text/html'>
    ${ name } is ${ age } years old
    <button data-bind='click: makeOlder'>Make older</button>
</script>

<script type='text/javascript'>
    var viewModel = {
        name: ko.observable('Bert'),
        age: ko.observable(78),
        makeOlder: function () {
            this.age(this.age() +1);
        }
    };
    ko.applyBindings(viewModel);
</script>

當引用的observable(dependent observable)數據改變的時候,Knockout會自動從新render模板。在這個例子裏,每次點擊button的時候都會從新render模板。

 

語法

你可使用任何你模板引擎支持的語法。jquery.tmpl執行以下語法:

${ someValue } — 參考文檔

{{html someValue}} — 參考文檔

{{if someCondition}} — 參考文檔

{{else someCondition}} — 參考文檔

{{each someArray}} — 參考文檔

 

和observable數組一塊兒使用{{each}}

固然使用{{each someArray}}的時候,若是你的值是observableArray,你必須使用JavaScript類型的基礎數組類型{{each myObservableArray()}},而不是{{each myObservableArray}}。

 

參數

    主參數

        語法快速記憶:若是你聲明的僅僅是字符串(上個例子),KO會使用模板的ID來render。應用在模板上的數據是你的整個view model對象(例如ko.applyBindings 綁定的對象)。

        更多控件,你能夠傳帶有以下屬性的JavaScript對象:

            name(必選項) — 須要render的模板ID – 參考 注5 如何使用function函數聲明ID。

            data(可選項) — 須要render到模板的數據。若是你忽略整個參數,KO將查找foreach參數,或者是應用整個view model對象。

            foreach(可選項) — 指定KO按照「foreach」模式render模板 – 參考 注3。

            afterAdd或beforeRemove(可選項) — 在foreach模式下使用callback函數。

            templateOptions(可選項) — 在render模板的時候,傳遞額外數據以便使用。參考 注6。

 

傳遞多個參數的例子:

<div data-bind='template: { name: "personTemplate", data: someObject }'> </div>

 

注1:Render嵌套模板

由於在模板裏使用的是data-bind屬性來聲明的,因此嵌套模板你能夠再次使用data-bind='template: ...',在上層模板的元素裏。

這比模板引發的原生語法好用多了(例如jquery.tmpl裏的{{tmpl}})。Knockout語法的好處在於能夠在每層模板的跟着相關的依賴值,因此若是依賴改變了,KO將只會從新render依賴所在的那個模板。這將很大地改善了性能。

 

注2:${ val }和<span data-bind='text: val'></span>有何不一樣?

當你在模板內部使用data-bind屬性的時候,KO是單獨爲這個綁定單獨跟蹤依賴項的。當model改變的時候,KO只會更新綁定的元素以及子元素而不須要從新render整個模板。因此若是你聲明這樣的代碼是<span data-bind='text: someObservableValue'></span>,當 someObservableValue改變的時候,KO將只是簡單地更新<span>元素的text值而不須要從新render整個模板。

不過,若是模板內部使用的observable值(例如${ someObservableValue }),若是這個observable值改變了,那KO將從新render整個模板。

這就是說,不少狀況下<span data-bind='text: someObservableValue'></span>性能要比${ someObservableValue }要好,由於值改變的話不會影響臨近元素的狀態。不過${ someObservableValue }語法比較簡潔,若是你的模板比較小的話,仍是更合適的,不會帶來大的性能問題。

 

注3:使用foreach

若是須要爲集合裏的每個item render一次模板,有2種方式:

    你可使用模板引擎裏的原生「each」語法,對jquery.tmpl來講就是用{{each}}語法迭代數組。

    另一種方式就是用Knockout的foreach模式來render。

 

例子:

<div data-bind='template: { name: "personTemplate",
                            foreach: someObservableArrayOfPeople }'> </div>

 

foreach模板模式的好處是:

當往你的collection集合裏添加新item項的時候,KO只會對這個新item進行render模板,而且將結果附加到現有的DOM上。

當從collection集合裏刪除item的時候,KO將不會從新render任何模板,而只是簡單地刪除相關的元素。

KO容許經過自定義的方式聲明afterAdd和beforeRemove的callback函數添加/刪除DOM元素。而後這個callback會在刪除元素的時候進行一些動畫或者其它操做。

與原生的each不一樣之處是:在改變以後,模板引擎強制從新render模板裏全部的內容,由於它根本就不關注KO裏所謂的依賴跟蹤內容。

關於使用foreach模式的例子,參考grid editor和animated transitions。

 

注4:使用afterRender選項

有時候,你須要在模板生成的DOM元素上深度定義邏輯。例如,你可能想再模板輸出的時候進行截獲,而後在render的元素上容許jQuery UI命令(好比date picker,slider,或其它)。

你可使用afterRender選項,簡單聲明一個function函數(匿名函數或者view model裏的函數),在render或者從新render模板以後Knockout會從新調用它。若是你使用的是foreach,那在每一個item添加到observable數組以後, Knockout會當即調用afterRender的callback函數。例如,

<div data-bind='template: { name: "personTemplate",
                            data: myData,
                            afterRender: myPostProcessingLogic }'> </div>

… 在view model裏聲明一個相似的函數(例如,對象包含myData):

viewModel.myPostProcessingLogic = function (elements) {
    // "elements" is an array of DOM nodes just rendered by the template
    // You can add custom post-processing logic here
}

 

注5:動態決定使用哪一個模板

有時候,你可能須要根據數據的狀態來決定使用哪一個模板的ID。能夠經過function的返回ID應用到name選擇上。若是你用的是foreach模板模式, Knockout會對每一個item執行function(將item做爲參數)從而將返回值做爲ID,不然,該function接受的參數是整個 data option或者是整個view model。

 

例子:

<ul data-bind='template: { name: displayMode,
                           foreach: employees }'> </ul>
<script type='text/javascript'>
var viewModel = {
    employees: ko.observableArray([
        { name: "Kari", active: ko.observable(true) },
        { name: "Brynn", active: ko.observable(false) },
        { name: "Nora", active: ko.observable(false) }
    ]),
    displayMode: function (employee) {
        return employee.active() ?"active" : "inactive";
        // Initially "Kari" uses the "active" template, while the others use "inactive"
    }
};

// ... then later ...
viewModel.employees()[1].active(true);
// Now "Brynn" is also rendered using the "active" template.
</script>

若是你的function引用的是observable值,那當這些值改變的時候,綁定的值會隨着改變的。這將致使相應的模板從新render。

 

注6:使用templateOptions傳遞額外的參數

若是你在綁定模板的時候須要傳入額外的數據的話,你可使用templateOptions對象來傳遞這些值。這能夠幫助你經過一些 不屬於view model過濾條件或者字符來重用模板。另一個好處是用在範圍控制,你能夠引用經過你的模板訪問怒道的數據。

 

例子,

<ul data-bind='template: { name: "personTemplate",
                           foreach: employees,
                           templateOptions: { label: "Employee:",
                                              selectedPerson: selectedEmployee } }'> </ul>


<script id='personTemplate' type='text/html'>
    <div data-bind="css: { selected: $data === $item.selectedPerson()" }">
        ${ $item.label } <input data-bind="value: name" />
    </div>
</script>

在整個例子裏,personTemplate有可能都使用employee和自定義對象。經過templateOptions咱們能夠傳遞一個字符label和當前已選擇項做爲selectedPerson來控制style。在jquery.tmpl模板裏,這些值能夠經過訪問$item對象的屬性獲得。

 

注7:模板是被預編譯和緩存的

爲了最大性能,Knockout內嵌模板引擎jquery.tmpl會利用自身的功能對你的模板進行預編譯成可執行的JavaScript代碼,而後從編譯流程裏緩存輸出。這將使模板更快更加具備可執行性,尤爲是是使用foreach循環來render相同模板的時候。

通常狀況你不會注意到這個,因此常常會忘記。不過,當你在某種緣由下經過編程重寫模板<script>元素的時候而且該模板以前已經用過一次的話,你的改變不會帶來任何render的變化,由於在第一次使用的時候已經預編譯了而且緩存起來了。(若是這些會帶來問題,咱們將考慮在KO新版本里提供一個禁用或重設模板緩存的功能,不過好像沒有好的緣由去動態改變模板<script>元素的內容)。

 

注8:使用不一樣的模板引擎

若是你想使用不一樣的JavaScript模板引擎(或者是由於某些緣由你不想在jQuery上使用依賴)。咱們能夠去爲KO來寫一個不一樣的模板引擎,例如,在KO源代碼裏的jqueryTmplTemplateEngine.js,儘管他是爲了支持多個版本的jquery.tmpl而編譯。支持一個單獨的模板引擎版本相對簡單多了。

 

依賴性

template綁定只能在引用合適的模板引擎狀況下才能工做。例如提到的jquery.tmpl引擎。

Knockout應用開發指南 第五章:建立自定義綁定

2011-11-26 19:20 by 湯姆大叔, 9751 閱讀, 3 評論, 收藏, 編輯

建立自定義綁定

你能夠建立本身的自定義綁定 – 沒有必要非要使用內嵌的綁定(像click,value等)。你能夠你封裝複雜的邏輯或行爲,自定義很容易使用和重用的綁定。例如,你能夠在form表單裏自定義像grid,tabset等這樣的綁定。

重要:如下文檔只應用在Knockout 1.1.1和更高版本,Knockout 1.1.0和之前的版本在註冊API上是不一樣的。

 

註冊你的綁定

添加子屬性到ko.bindingHandlers來註冊你的綁定:

ko.bindingHandlers.yourBindingName = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        // This will be called when the binding is first applied to an element
        // Set up any initial state, event handlers, etc. here
    },

    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        // This will be called once when the binding is first applied to an element,
        // and again whenever the associated observable changes value.
        // Update the DOM element based on the supplied values here.
    }
};

… 而後就能夠在任何DOM元素上使用了:

<div data-bind="yourBindingName: someValue"> </div>

注:你實際上不必把init和update這兩個callbacks都定義,你能夠只定義其中的任意一個。

 

update 回調

當管理的observable改變的時候,KO會調用你的update callback函數,而後傳遞如下參數:

element — 使用這個綁定的DOM元素

valueAccessor —JavaScript函數,經過valueAccessor()能夠獲得應用到這個綁定的model上的當前屬性值。

allBindingsAccessor —JavaScript函數,經過allBindingsAccessor ()獲得這個元素上全部model的屬性值。

viewModel — 傳遞給ko.applyBindings使用的 view model參數,若是是模板內部的話,那這個參數就是傳遞給該模板的數據。

 

例如,你可能想經過 visible綁定來控制一個元素的可見性,可是你想讓該元素在隱藏或者顯示的時候加入動畫效果。那你能夠自定義本身的綁定來調用jQuery的slideUp/slideDown 函數:

ko.bindingHandlers.slideVisible = {
    update: function(element, valueAccessor, allBindingsAccessor) {
        // First get the latest data that we're bound to
        var value = valueAccessor(), allBindings = allBindingsAccessor();       

        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.utils.unwrapObservable(value);

        // Grab some more data from another binding property
        var duration = allBindings.slideDuration || 400;

        // 400ms is default duration unless otherwise specified
    
        // Now manipulate the DOM element

        if (valueUnwrapped == true)
            $(element).slideDown(duration); // Make the element visible
        else
            $(element).slideUp(duration);   // Make the element invisible
    }
};

 

而後你能夠像這樣使用你的綁定:

<div data-bind="slideVisible: giftWrap, slideDuration:600">You have selected the option</div>
<label><input type="checkbox" data-bind="checked: giftWrap"/> Gift wrap</label>

<script type="text/javascript">
    var viewModel = {
        giftWrap: ko.observable(true)
    };
    ko.applyBindings(viewModel);
</script>

固然,看來可能代碼不少,可是一旦你建立了自定義綁定,你就能夠在不少地方重用它。

 

init 回調

Knockout在DOM元素使用自定義綁定的時候會調用你的init函數。init有兩個重要的用途:

爲DOM元素設置初始值

註冊事件句柄,例如當用戶點擊或者編輯DOM元素的時候,你能夠改變相關的observable值的狀態。

 KO會傳遞和update回調函數同樣的參數。

繼續上面的例子,你能夠像讓slideVisible在頁面第一次顯示的時候設置該元素的狀態(可是不使用任何動畫效果),而只是讓動畫在之後改變的時候再執行。你能夠這樣來作:

ko.bindingHandlers.slideVisible = {
    init: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        // Get the current value of the current property we're bound to
        $(element).toggle(value);
        // jQuery will hide/show the element depending on whether "value" or true or false
    },

    update: function(element, valueAccessor, allBindingsAccessor) {
        // Leave as before
    }
};

這就是說giftWrap的初始值聲明的是false(例如giftWrap: ko.observable(false)),而後讓初始值會讓關聯的DIV隱藏,以後用戶點擊checkbox的時候會讓元素顯示出來。

 

DOM事件以後更新observable值

你已經值得了如何使用update回調,當observable值改變的時候,你能夠更新相關的DOM元素。可是其它形式的事件怎麼作呢?好比當用戶對某個DOM元素有某些action操做的時候,你想更新相關的observable值。

你可使用init回調來註冊一個事件句柄,這樣能夠改變相關的observable值,例如,

ko.bindingHandlers.hasFocus = {

    init: function (element, valueAccessor) {
        $(element).focus(function () {
            var value = valueAccessor();
            value(true);
        });

        $(element).blur(function () {
            var value = valueAccessor();
            value(false);
        });
    },

    update: function (element, valueAccessor) {
        var value = valueAccessor();
        if (ko.utils.unwrapObservable(value))
            element.focus();
        else
            element.blur();
    }
};

如今你能夠經過hasFocus綁定來讀取或者寫入這個observable值了:

<p>Name: <input data-bind="hasFocus: editingName"/></p>
<!-- Showing that we can both read and write the focus state -->
<div data-bind="visible: editingName">You're editing the name</div>
<button data-bind="enable: !editingName(), click:function() { editingName(true) }">Edit name</button>

<script type="text/javascript">
    var viewModel = {
        editingName: ko.observable()
    };
    ko.applyBindings(viewModel);
</script>

 

Knockout應用開發指南 第六章:加載或保存JSON數據

2011-11-28 09:02 by 湯姆大叔, 12004 閱讀, 3 評論, 收藏, 編輯

加載或保存JSON數據

Knockout能夠實現很複雜的客戶端交互,可是幾乎全部的web應用程序都要和服務器端交換數據(至少爲了本地存儲須要序列化數據),交換數據最方便的就是使用JSON格式 – 大多數的Ajax應用程序也是使用這種格式。

 

加載或保存數據

Knockout不限制你用任何技術加載和保存數據。你可使用任何技術和服務器來交互。用的最多的是使用jQuery的Ajax幫助,例如:getJSON,post和ajax。你能夠經過這些方法從服務器端獲取數據:

$.getJSON("/some/url", function (data) {
    // Now use this data to update your view models,
    // and Knockout will update your UI automatically
})

… 或者向服務器端發送數據:

var data = /* Your data in JSON format - see below */;
$.post("/some/url", data, function(returnedData) {
    // This callback is executed if the post was successful  
})

或者,若是你不想用jQuery,你能夠用任何其它的方式來讀取或保存JSON數據。因此, Knockout須要你作的僅僅是:

    對於保存,讓你的view model數據轉換成簡單的JSON格式,以方便使用上面的技術來保存數據。

    對於加載,更新你接收到的數據到你的view model上。

 

轉化View Model數據到JSON格式

因爲view model都是JavaScript對象,因此你須要使用標準的JSON序列化工具讓轉化view model爲JSON格式。例如,可使用JSON.serialize()(新版本瀏覽器才支持的原生方法),或者使用json2.js類庫。不過你的view model可能包括observables,依賴對象dependent observables和observable數組,有可能不能很好的序列化,你須要本身額外的處理一下數據。

 

爲了使view model數據序列化方便(包括序列化observables等格式),Knockout提供了2個幫助函數:

    ko.toJS — 克隆你的view model對象,而且替換全部的observable 對象爲當前的值,這樣你能夠獲得一個乾淨的和Knockout無關的數據copy。

    ko.toJSON — 將view model對象轉化成JSON字符串。原理就是:先調在view model上調用ko.toJS,而後調用瀏覽器原生的JSON 序列化器獲得結果。注:一些老瀏覽器版本不支持原生的JSON 序列化器(例如:IE7和之前的版本),你須要引用json2.js類庫。

 

聲明一個view model:

var viewModel = {
    firstName: ko.observable("Bert"),
    lastName: ko.observable("Smith"),
    pets: ko.observableArray(["Cat", "Dog", "Fish"]),
    type: "Customer"
};

viewModel.hasALotOfPets = ko.dependentObservable(function () {
    return this.pets().length > 2
}, viewModel)

該view model包含observable類型的值,依賴類型的值dependent observable以及依賴數組observable array,和普通對象。你能夠像以下代碼同樣使用ko.toJSON將此轉化成服務器端使用的JSON 字符串:

var jsonData = ko.toJSON(viewModel);

// Result: jsonData is now a string equal to the following value
// '{"firstName":"Bert","lastName":"Smith","pets":["Cat","Dog","Fish"],"type":"Customer","hasALotOfPets":true}'

或者,序列化以前,你想獲得JavaScript簡單對象的話,直接使用像這樣同樣使用ko.toJS:

var plainJs = ko.toJS(viewModel);

// Result: plainJS is now a plain JavaScript object in which nothing is observable. It's just data.
// The object is equivalent to the following:
//   {
//      firstName: "Bert",
//      lastName: "Smith",
//      pets: ["Cat","Dog","Fish"],
//      type: "Customer",
//      hasALotOfPets: true
//   }

 

使用JSON更新View Model數據

若是你從服務器端獲取數據而且更新到view model上,最簡單的方式是本身實現。例如,

// Load and parse the JSON
var someJSON = /* Omitted: fetch it from the server however you want */;
var parsed = JSON.parse(someJSON);

// Update view model properties
viewModel.firstName(parsed.firstName);
viewModel.pets(parsed.pets);

不少狀況下,最直接的方法就是最簡單並且最靈活的方式。固然,若是你更新了view model的屬性,Knockout會自動幫你更新相關的UI元素的。

 

不過,不少開發人員仍是喜歡使用一種好用而不是每次都寫代碼的方式來轉化數據到view model上,尤爲是view model有不少屬性或者嵌套的數據結構的時候,這頗有用,由於能夠節約不少代碼量。knockout.mapping插件能夠幫你作到這一點。

Knockout應用開發指南 第七章:Mapping插件

2011-11-29 09:08 by 湯姆大叔, 12908 閱讀, 16 評論, 收藏, 編輯

Mapping插件

Knockout設計成容許你使用任何JavaScript對象做爲view model。必須view model的一些屬性是observable的,你可使用KO綁定他們到你的UI元素上,當這些observable值改變的時候,這些UI元素就會自動更新。

絕大多數程序都須要從服務器端獲取數據,可是因爲服務器不知道observable的概念是什麼,它只支持簡單的JavaScript對象(一般是 序列化之後的JSON),mapping插件可讓你很方便地將簡單JavaScript對象mapp到帶有observable值的view model。你也能夠本身寫JavaScript代碼將從服務器獲取的數據來構建 view model,mapping插件只是一種很好的替代而已。

 下載

    Version 2.0 (最小版本8.6kb)

 

例子:手工mapping

顯示當前服務器時間和你網站上的當前用戶數。你應該使用以下的view model來表明你的這些信息:

var viewModel = {
    serverTime: ko.observable(),
    numUsers: ko.observable()
}

而後綁定view model到HTML元素上,以下:

The time on the server is: <span data-bind='text: serverTime'></span>
and <span data-bind='text: numUsers'></span> user(s) are connected.

因爲view model屬性是observable的,在他們變化的時候,KO會自動更新綁定的HTML元素。

接下來,從服務器獲取最新的數據。或許每隔5秒你要調用一次Ajax請求(例如,使用jQuery的$.getJSON或$.ajax函授):

var data = getDataUsingAjax();          // Gets the data from the server

而後,服務器返回和下面類似的JSON數據:

{
    serverTime: '2010-01-07',
    numUsers: 3
}

最後,用這些數據更新你的view model(不使用mapping插件),代碼以下:

// Every time data is received from the server:
viewModel.serverTime(data.serverTime);
viewModel.numUsers(data.numUsers);

爲了使數據顯示在頁面上,全部的屬性都要像這樣寫代碼。若是你的數據結構很複雜的話(例如,包含子對象或者數組),那就維護起來就至關痛苦。mapping插件就是來讓你讓你的JavaScript簡單對象(或JSON結構)轉換成observable的view model的。

 

例子:使用ko.mapping

經過mapping插件建立view model,直接使用ko.mapping.fromJS函數來建立:

var viewModel = ko.mapping.fromJS(data);

它會自動將data裏全部的屬性建立成observable類型的屬性。你能夠經過ko.mapping.fromJS 函數按期從服務器獲取數據,而後更新你的view model:

// Every time data is received from the server:
ko.mapping.fromJS(data, viewModel);

 

如何mapping?

    對象的全部屬性都被轉換成observable類型值,若是獲取的對象的值改變了,就會更新這個observable類型的值.

    數組也被轉換成了observable數組,若是服務器更新改變了數組的個數,mapping插件也會添加或者刪除相應的item項,也會盡可能保持和原生JavaScript數組相同的order順序。

 

Unmapping

若是你想讓map過的對象轉換成原來的JavaScript對象,使用以下方式:

var unmapped = ko.mapping.toJS(viewModel);

會建立一個unmapped對象,只包含你以前map過的對象屬性,換句話說,你在view model上手工添加的屬性或者函數都會被忽略的,惟一例外的是_destroy屬性是能夠unmapped回來的,由於你從ko.observableArray裏destroy一個item項的時候會生成這個屬性。 請參考「高級用戶」小節如何配置使用。

 

與JSON字符串一塊兒使用

若是你的Ajax調用返回的是JSON字符串(而不是反序列化後的JavaScript對象),你可使用ko.mapping.fromJSON函數來建立或者更新你的view model。用ko.mapping.toJSON實現unmap。

使用.from/toJSON函數處理JSON字符串和使用.from/toJS函數處理JS對象是等價的。

 

高級用法

有時候,在使用ko.mapping.fromJS的時候,可能有必要去使用mapping的高級用法來定義mapping的詳細過程,之後定義了,之後再調用的時候就沒必要再定義了。這裏有一些情形,你可能須要使用這些option。

 

用法1:使用keys來使對象unique化

你有一個JavaScript對象,以下:

var data = {
    name: 'Scot',
    children: [
        { id: 1, name: 'Alicw' }
    ]
}

使用map插件,你能夠將它map到view model上(沒任何問題):

var viewModel = ko.mapping.fromJS(data);

如今,數據被更新成以下這樣:

var data = {
    name: 'Scott',
    children: [
        { id: 1, name: 'Alice' }
    ]
}

這裏發生了兩件事:name從Scot變成了Scott,children[0].name從Alicw變成了Alice。你能夠用以下代碼更新view model:

ko.mapping.fromJS(data, viewModel);

因而,name像咱們指望的同樣更新了,可是在children數組裏,子項Alicw被刪除而新項Alice被添加到數組裏。這不是咱們所指望的,咱們指望的是隻是把name從Alicw更新成Alice,不是替換整個item項。發生的緣由是,默認狀況下mapping plugin插件只是簡單地比較數組裏的兩個對象是否相等。 由於JavaScript裏{ id : 1, name : 'Alicw' }和{ id : 1, name : 'Alice' }是不相等的,因此它認爲喜歡將新項替換掉老項。

解決這個問題,你須要聲明一個key讓mapping插件使用,用來判斷一個對象是新對象仍是舊對象。代碼以下:

var mapping = {
    'children': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.id);
        }
    }
}

var viewModel = ko.mapping.fromJS(data, mapping);

這樣,每次map的時候,mapping插件都會檢查數組項的id屬性來判斷這個數組項是須要合併的仍是全新replace的。

 

用法2:用create自定義對象的構造器

若是你想本身控制mapping,你也可使用create回調。使用回調可讓你本身控制mapping。

舉例,你有一個像這樣的JavaScript對象:

var data = {
    name: 'Graham',
    children: [
        { id: 1, name: 'Lisa' }
    ]
}

若是你想本身map children 數組,你能夠這樣聲明:

var mapping = {
    'children': {
        create: function (options) {
            return new myChildModel(options.data);
        }
    }
}

var viewModel = ko.mapping.fromJS(data, mapping);

支持create回調的options參數是一個JavaScript對象,包含以下:

data: JavaScript對象,包含child用到的數據

parent:child對象所屬的父對象或者數組

 固然,在內部的create回調裏,你也能夠再次調用ko.mapping.fromJS。一個例子就是:若是你想讓初始的JavaScript對象帶有額外的依賴屬性dependent observables:

var myChildModel = function (data) {
    ko.mapping.fromJS(data, {}, this);

    this.nameLength = ko.dependentObservable(function () {
        return this.name().length;
    }, this);
}

 

用法3:用update自定義對象的updating

你也可使用update 回調來自定義一個對象如何更新。它接受一個須要替代的對象以及和create 回調同樣的options參數,你應該return更新後的值。

update 回調使用的options參數是一個JavaScript對象,包含以下內容:

data:JavaScript對象,包含child用到的數據

parent:child對象所屬的父對象或者數組

observable:若是屬性是observable的,這將會寫入到實際的observable裏

 例子,在數據顯示以前,在新數據後面附加額外的字符串:

var data = {
    name: 'Graham',
}

var mapping = {
    'name': {
        update: function(options) {
            return options.data + 'foo!';
        }
    }
}

var viewModel = ko.mapping.fromJS(data, mapping);
alert(viewModel.name());

alert的結果是:Grahamfoo!。

 

用法4:使用ignore忽略不須要map的屬性

若是在map的時候,你想忽略一些屬性,你可使用ignore累聲明須要忽略的屬性名稱集合:

var mapping = {
    'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}

var viewModel = ko.mapping.fromJS(data, mapping);

你聲明的忽略數組被編譯到默認的ignore數組裏。你能夠像下面代碼同樣來維護它:

var oldOptions = ko.mapping.defaultOptions().ignore;
ko.mapping.defaultOptions().ignore = ["alwaysIgnoreThis"];

 

用法5:使用include聲明須要map的屬性

默認狀況下,當map你的view model回到JS對象是時候,只map原始view model裏擁有的屬性(除了例外的_destroy屬性),不過,你可使用include參數來定製:

var mapping = {
    'include': ["propertyToInclude", "alsoIncludeThis"]
}

var viewModel = ko.mapping.fromJS(data, mapping);

你聲明的include數組被編譯到默認的include數組裏,默認只有_destroy。 你能夠像這樣來維護:

var oldOptions = ko.mapping.defaultOptions().include;
ko.mapping.defaultOptions().include = ["alwaysIncludeThis"];

 

用法6:使用copy來複制屬性

默認狀況下,map的時候是把全部的值都轉換成observable的,若是你只是想copy屬性值而不是替換成observable的,你能夠將屬性名稱添加到copy數組:

var mapping = {
    'copy': ["propertyToCopy"]
}

var viewModel = ko.mapping.fromJS(data, mapping);

你聲明的copy數組被編譯到默認的copy數組裏,默認值是空。你能夠像這樣來維護:

var oldOptions = ko.mapping.defaultOptions().copy;
ko.mapping.defaultOptions().copy = ["alwaysCopyThis"];

 

用法7:Specifying the update target

在上面的例子,若是你想再一個class內map,你可使用第三個參數做爲操做的目標,例如:

ko.mapping.fromJS(data, {}, someObject); // overwrites properties on someObject

因此,若是你想map一個JavaScript對象到this上,你能夠這樣聲明:

ko.mapping.fromJS(data, {}, this);

 

從多數據源map

你能夠經過屢次使用ko.mapping.fromJS 來將多個JS對象的數據源map到一塊兒,例如:

var viewModel = ko.mapping.fromJS(alice, aliceMappingOptions);
ko.mapping.fromJS(bob, bobMappingOptions, viewModel);

你聲明的mapping選項option在每次調用的時候都會合並。

 

Map之後的observable數組

map插件map之後生產的observable數組,帶有幾個額外的函數來處理帶有keys的mapping:

mappedRemove

mappedRemoveAll

mappedDestroy

mappedDestroyAll

mappedIndexOf

 它們是和ko.observableArray裏的函數等價的,不一樣是他們經過key來處理對象。例如:

var obj = [
    { id: 1 },
    { id: 2 }
]

var result = ko.mapping.fromJS(obj, {
    key: function (item) {
        return ko.utils.unwrapObservable(item.id);
    }
});

result.mappedRemove({ id: 2 });

map過的observable數組,除了上面的函數還支持一個mappedCreate函數:

var newItem = result.mappedCreate({ id: 3 });

首先會檢查key(id=3)在數組裏是否存在(若是存在則拋出異常),而後,若是有create和 update回調的話會調用他們,最後建立一個新對象,並將新對象添加到數組而後返回該新對象。

Knockout應用開發指南 第八章:簡單應用舉例(1)

2011-11-30 09:04 by 湯姆大叔, 11697 閱讀, 5 評論, 收藏, 編輯

本章展現的4個例子主要是利用了Knockout的基本語法特性,讓你們感覺到使用Kncokout的快感。

1   Hello world

這個例子裏,2個輸入框都被綁定到data model上的observable變量上。「full name」顯示的是一個dependent observable,它的值是前面2個輸入框的值合併一塊兒的結果。

 

 不管哪一個輸入框更新,都會看到「full name」 顯示結果都會自動更新。查看HTML源代碼能夠看到咱們不須要聲明onchange事件。Knockout知道何時該更新UI。

 

代碼: View

<p>First name: <input data-bind="value: firstName"/></p>
<p>Last name: <input data-bind="value: lastName"/></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>

代碼: View model

// 這裏是聲明的view model

var viewModel = {
    firstName: ko.observable("Planet"),
    lastName: ko.observable("Earth")
};

viewModel.fullName = ko.dependentObservable(function () {
    // Knockout tracks dependencies automatically.
    //It knows that fullName depends on firstName and lastName,          
    //because these get called when evaluating fullName.
    return viewModel.firstName() + " " + viewModel.lastName();
});

ko.applyBindings(viewModel); // This makes Knockout get to work

 

2   Click counter

這個例子展現的建立一個view model而且綁定各類綁定到HTML元素標記上,以便展現和修改view model的狀態。

Knockout根據依賴項。在內部,hasClickedTooManyTimes在numberOfClicks上有個訂閱,以便當numberOfClicks改變的時候,強制hasClickedTooManyTimes從新執行。類似的,UI界面上多個地方引用hasClickedTooManyTimes,因此當hasClickedTooManyTimes 改變的時候,也講致使UI界面更新。

不須要手工聲明或者訂閱這些subscription訂閱,他們由KO框架本身建立和銷燬。參考以下代碼實現:

 

 

代碼: View

<div>You've clicked <span data-bind="text: numberOfClicks">&nbsp;</span> times</div>

<button data-bind="click: registerClick, enable: !hasClickedTooManyTimes()">Click me</button>

<div data-bind="visible: hasClickedTooManyTimes">
    That's too many clicks! Please stop before you wear out your fingers.
    <button data-bind="click: function() { numberOfClicks(0) }">Reset clicks</button>
</div>

代碼: View model

var clickCounterViewModel = function () {
    this.numberOfClicks = ko.observable(0);

    this.registerClick = function () {
        this.numberOfClicks(this.numberOfClicks() + 1);
    }

    this.hasClickedTooManyTimes = ko.dependentObservable(function () {
        return this.numberOfClicks() >= 3;
    }, this);
};

ko.applyBindings(new clickCounterViewModel());

 

3   Simple list

這個例子展現的是綁定到數組上。

注意到,只有當你在輸入框裏輸入一些值的時候,Add按鈕纔可用。參考下面的HTML代碼是如何使用enable 綁定。

 

 

代碼: View

<form data-bind="submit: addItem">
    New item:
    <input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' />
    <button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
    <p>Your items:</p>
    <select multiple="multiple" width="50" data-bind="options: items"> </select>
</form>

代碼: View model

var viewModel = {};
viewModel.items = ko.observableArray(["Alpha", "Beta", "Gamma"]);
viewModel.itemToAdd = ko.observable("");
viewModel.addItem = function () {
    if (viewModel.itemToAdd() != "") {
        viewModel.items.push(viewModel.itemToAdd());
        // Adds the item. Writing to the "items" observableArray causes any associated UI to update.

        viewModel.itemToAdd("");                 
        // Clears the text box, because it's bound to the "itemToAdd" observable
    }
}

ko.applyBindings(viewModel);

 

4   Better list

這個例子是在上個例子的基礎上添加remove item功能(multi-selection)和排序功能。 「remove」和「sort」按鈕在不能用的時候會變成disabled狀態(例如,沒有足夠的item來排序)。

參考HTML代碼是如何實現這些功能的,另外這個例子也展現瞭如何使用匿名函數來實現排序。

 

 

代碼: View

<form data-bind="submit:addItem">
    Add item: <input type="text" data-bind='value:itemToAdd, valueUpdate: "afterkeydown"' />
    <button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
</form>

<p>Your values:</p>
<select multiple="multiple" height="5" data-bind="options:allItems, selectedOptions:selectedItems"> </select>

<div>
    <button data-bind="click: removeSelected, enable: selectedItems().length > 0">Remove</button>
    <button data-bind="click: function() { allItems.sort() }, enable: allItems().length > 1">Sort</button>
</div>

代碼: View model

// In this example, betterListModel is a class, and the view model is an instance of it.

// See simpleList.html for an example of how to construct a view model without defining a class for it. Either technique works fine.

var betterListModel = function () {
    this.itemToAdd = new ko.observable("");
    this.allItems = new ko.observableArray(["Fries", "Eggs Benedict", "Ham", "Cheese"]);

// Initial items

this.selectedItems = new ko.observableArray(["Ham"]);                              

// Initial selection

    this.addItem = function () {
        if ((this.itemToAdd() != "") && (this.allItems.indexOf(this.itemToAdd()) < 0))
    // Prevent blanks and duplicates
        this.allItems.push(this.itemToAdd());
        this.itemToAdd(""); // Clear the text box
    }

    this.removeSelected = function () {
        this.allItems.removeAll(this.selectedItems());
        this.selectedItems([]); // Clear selection
    }
};

ko.applyBindings(new betterListModel());

 

Knockout應用開發指南 第八章:簡單應用舉例(2)

2011-12-01 09:47 by 湯姆大叔, 8437 閱讀, 8 評論, 收藏, 編輯

5   Control types

這個例子,對view model沒有什麼特殊的展現,只是展現如何綁定到各類元素上(例如,select, radio button等)。

 

代碼: View

View Code

 

代碼: View model

var viewModel = {
    stringValue: ko.observable("Hello"),
    passwordValue: ko.observable("mypass"),
    booleanValue: ko.observable(true),
    optionValues: ["Alpha", "Beta", "Gamma"],
    selectedOptionValue: ko.observable("Gamma"),
    multipleSelectedOptionValues: ko.observable(["Alpha"]),
    radioSelectedOptionValue: ko.observable("Beta")
};

ko.applyBindings(viewModel);

 

6   Templating

這個例子展現的render模板,以及在模板內部如何使用data binding屬性的。

Template很容易嵌套,當任何依賴數據改變的時候,Knockout會自動從新render模板。參考演示(啓用‘Show render times’),Knockout知道只須要從新render改變的那些數據所綁定的最近的模板。

 

代碼: View

<div data-bind='template: "peopleTemplate"'>
</div>
<label>
    <input type="checkbox" data-bind="checked: showRenderTimes"/>
    Show render times</label>
<script type="text/html" id="peopleTemplate">
<h2>People</h2>
<ul>
  {{each people}}
  <li>
  <div>
    ${ name } has <span data-bind="text: children().length">&nbsp;</span> children:
    <a href="#" data-bind="click: addChild ">Add child</a>
    <span class="renderTime" data-bind="visible: showRenderTimes">
                        (person rendered at <span data-bind="text: new Date().getSeconds()"></span>)
    </span>
  </div>
  <div data-bind='template: { name: "childrenTemplate", data: children }'></div>
  </li>
{{/each}}
</ul>
</script>
<script type="text/html" id="childrenTemplate">
    <ul>
        {{each $data}}
            <li>
                ${ this }
                <span class="renderTime" data-bind="visible: viewModel.showRenderTimes">
                    (child rendered at <span data-bind="text: new Date().getSeconds()"></span>)
               </span>
           </li>
        {{/each}}
</ul>
</script>

代碼: View model

// Define a "person" class that tracks its own name and children, and has a method to add a new child

var person = function (name, children) {
    this.name = name;
    this.children = ko.observableArray(children);

    this.addChild = function () {
        this.children.push("New child");
    } .bind(this);
}

// The view model is an abstract description of the state of the UI, but without any knowledge of the UI technology (HTML)

var viewModel = {
    people: [
        new person("Annabelle", ["Arnie", "Anders", "Apple"]),
        new person("Bertie", ["Boutros-Boutros", "Brianna", "Barbie", "Bee-bop"]),
        new person("Charles", ["Cayenne", "Cleopatra"])
    ],
    showRenderTimes: ko.observable(false)
};

ko.applyBindings(viewModel);

 

7   Paged grid

data-bind="..."綁定(像text, visible, 和click不是固定死的) - 你能夠很容易自定義本身的綁定。若是你的自定義綁定僅僅是添加事件或者更新DOM元素的屬性,那幾行就能夠實現。不過,你依然能夠自定義能夠重用的綁定(或插件),就行本例的simpleGrid綁定。

若是一個插件須要本身標準的view model(例如本例的ko.simpleGrid.viewModel ),它提供既提供了該如何配置插件實例(分頁大小,列聲明)工做,也提供了view model上的屬性是不是observable 的(例如currentpage索引)。也能夠擴展代碼讓這些屬性很容易地改變,而且讓UI自動更新。例如,「Jump to first page」按鈕的工做原理。

查看HTML源代碼能夠看到很是容易使用這個simple grid插件。simpleGrid源碼地址是:http://knockoutjs.com/examples/resources/knockout.simpleGrid.js

 

代碼: View

<div data-bind="simpleGrid: gridViewModel"> </div>

<button data-bind='click: function() { items.push({ name: "New item", sales: 0, price: 100 }) }'>
    Add item 
</button>

<button data-bind="click: sortByName">
    Sort by name
</button>

<button data-bind="click: function() { gridViewModel.currentPageIndex(0) }">
    Jump to first page
</button>

代碼: View model

var myModel = {
    items: ko.observableArray([
        { name: "Well-Travelled Kitten", sales: 352, price: 75.95 },
        { name: "Speedy Coyote", sales: 89, price: 190.00 },
        { name: "Furious Lizard", sales: 152, price: 25.00 },
        { name: "Indifferent Monkey", sales: 1, price: 99.95 },
        { name: "Brooding Dragon", sales: 0, price: 6350 },
        { name: "Ingenious Tadpole", sales: 39450, price: 0.35 },
        { name: "Optimistic Snail", sales: 420, price: 1.50 }
    ]),

    sortByName: function () {
        this.items.sort(function (a, b) {
            return a.name < b.name ? -1 : 1;
        });
    }
};

myModel.gridViewModel = new ko.simpleGrid.viewModel({
    data: myModel.items,
    columns: [
        { headerText: "Item Name", rowText: "name" },
        { headerText: "Sales Count", rowText: "sales" },
        { headerText: "Price", rowText: function (item) { return "$" + item.price.toFixed(2) } }
    ],
    pageSize: 4
});

ko.applyBindings(myModel);

 

8   Animated transitions

該例子展現了2種方式實現動畫過渡效果:

當使用template/foreach綁定的時候,你可使用afterAdd和beforeRemove回調函數,他們可讓你寫代碼真實操做添加和刪除元素,這樣你就可使用像jQuery的 slideUp/slideDown()這樣的動畫效果。在planet types之間切換或添加新的planet能夠看到效果。

經過observable 類型的值,咱們不難定義本身的Knockout綁定,查看HTML源代碼能夠看到一個自定義綁定fadeVisible,無論何時它改變了, jQuery就會在相關的元素上執行fadeIn/fadeOut動畫效果。點擊「advanced options」 checkbox 能夠看到效果。

 

 

 

代碼: View

<h2>Planets</h2>
<p>
    <label>
        <input type="checkbox" data-bind="checked: displayAdvancedOptions"/>
        Display advanced options
    </label>
</p>
<p data-bind="fadeVisible: displayAdvancedOptions">
    Show:
    <label><input type="radio" value="all" data-bind="checked: typeToShow"/>All</label>
    <label><input type="radio" value="rock" data-bind="checked: typeToShow"/>Rocky planets</label>
    <label><input type="radio" value="gasgiant" data-bind="checked: typeToShow"/>Gas giants</label>
</p>
<div data-bind='template: { name: "planetsTemplate",
                            foreach: planetsToShow,
                            beforeRemove: function(elem) { $(elem).slideUp(function() { $(elem).remove(); }) },
                            afterAdd: function(elem) { $(elem).hide().slideDown() } }'>
</div>
<script type="text/html" id="planetsTemplate">
    <div class="planet ${ type }">${ name }</div>
</script>
<p data-bind="fadeVisible: displayAdvancedOptions">
    <button data-bind='click: function() { addPlanet("rock") }'>
        Add rocky planet</button>
    <button data-bind='click: function() { addPlanet("gasgiant") }'>
        Add gas giant</button>
</p>

代碼: View model

var viewModel = {
    planets: ko.observableArray([
        { name: "Mercury", type: "rock" },
        { name: "Venus", type: "rock" },
        { name: "Earth", type: "rock" },
        { name: "Mars", type: "rock" },
        { name: "Jupiter", type: "gasgiant" },
        { name: "Saturn", type: "gasgiant" },
        { name: "Uranus", type: "gasgiant" },
        { name: "Neptune", type: "gasgiant" },
        { name: "Pluto", type: "rock" }
    ]),

    typeToShow: ko.observable("all"),
    displayAdvancedOptions: ko.observable(false),

    addPlanet: function (type) { this.planets.push({ name: "New planet", type: type }); }
};

viewModel.planetsToShow = ko.dependentObservable(function () {
    // Represents a filtered list of planets
    // i.e., only those matching the "typeToShow" condition

    var desiredType = this.typeToShow();

    if (desiredType == "all")
        return this.planets();

    return ko.utils.arrayFilter(this.planets(), function (planet) {
        return planet.type == desiredType;
    });
} .bind(viewModel));

// Here's a custom Knockout binding that makes elements shown/hidden via jQuery's fadeIn()/fadeOut() methods
// Could be stored in a separate utility library

ko.bindingHandlers.fadeVisible = {
    init: function (element, valueAccessor) {
        // Initially set the element to be instantly visible/hidden depending on the value
        var value = valueAccessor();

        $(element).toggle(ko.utils.unwrapObservable(value));
        // Use "unwrapObservable" so we can handle values that may or may not be observable
    },

    update: function (element, valueAccessor) {
        // Whenever the value subsequently changes, slowly fade the element in or out
        var value = valueAccessor();
        ko.utils.unwrapObservable(value) ? $(element).fadeIn() : $(element).fadeOut();
    }
};

ko.applyBindings(viewModel);

 

Knockout應用開發指南 第九章:高級應用舉例

2011-12-02 15:04 by 湯姆大叔, 12079 閱讀, 9 評論, 收藏, 編輯

1   Contacts editor

這個例子和微軟爲演示jQuery Data Linking Proposal例子提供的例子同樣的提供的,咱們能夠看看Knockout實現是難了仍是容易了。

代碼量的多少不重要(儘快Knockout 的實現很簡潔),重要的看起來是否容易理解且可讀。查看HTML源代碼,看看如何實現的view model以及綁定的。

 

 

代碼: View

View Code

 

代碼: View model

View Code

 

2   Editable grid

該例是使用「foreach」綁定爲數組裏的每一項來render到 template上。好處(相對於模板內部使用for循環)是當你添加或者刪除item項的時候,Knockout不須要從新render – 只須要render新的item項。就是說UI上其它控件的狀態(好比驗證狀態)不會丟失。

如何一步一步構建這個例子並集成ASP.NET MVC,請參閱此貼

 

 

代碼: View

View Code

 

代碼: View model

View Code

 

3   Shopping cart screen

這個例子展現的是依賴監控屬性(dependent observable)怎麼樣鏈在一塊兒。每一個cart對象都有一個dependentObservable對象去計算本身的subtotal,這些又被一 個進一步的dependentObservable對象依賴計算總的價格。當改變數據的時候,整個鏈上的依賴監控屬性都會改變,全部相關的UI元素也會被 更新。

這個例子也展現瞭如何建立聯動的下拉菜單。

 

 

代碼: View

View Code

 

代碼: View model

View Code

 

4   Twitter client

這是一個複雜的例子,展現了幾乎全部Knockout特性來構建一個富客戶端。

用戶數據存在一個JavaScript模型裏,經過模板來展現。就是說咱們能夠經過清理用戶列表裏的數據來達到隱藏用戶信息的目的,而不須要手動去隱藏DOM元素。

按鈕將根據他們是否可操做來自動變成enabled或disabled狀態。例如,有一個叫hasUnsavedChanges的依賴監控屬性(dependentObservable)控制這「Save」按鈕的enabled狀態。

能夠很是方便地從外部JSON服務獲取數據,並集成到view model裏,而後顯示在頁面上。

 

 

代碼: View

 

 

<div class="loadingIndicator">
    Loading...</div>
<div class="configuration">
    <div class="listChooser">
        <button data-bind='click: deleteList, enable: editingList.name'>
            Delete</button>
        <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>
            Save</button>
        <select data-bind='options: savedLists, optionsValue: "name", value: editingList.name'>
        </select>
    </div>
    <p>
        Currently viewing <span data-bind="text: editingList.userNames().length">&nbsp;</span>
        user(s):</p>
    <div class="currentUsers" data-bind='template: { name: "usersTemplate", data: editingList }'>
    </div>
    <form data-bind="submit: addUser">
    <label>
        Add user:</label>
    <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
    <button type="submit" data-bind='enable: userNameToAddIsValid() && userNameToAdd() != ""'>
        Add</button>
    </form>
</div>
<div class="tweets" data-bind='template: { name: "tweetsTemplate", data: currentTweets }'>
</div>
<script type="text/html" id="tweetsTemplate">
    <table width="100%">
        {{each $data}}
            <tr>
                <td><img src="${ profile_image_url }"/></td>
                <td>
                    <a class="twitterUser" href="http://twitter.com/${ from_user }">${ from_user }</a>
                    ${ text }
                    <div class="tweetInfo">${ created_at }</div>
</td>
</tr>
        {{/each}}
</table>
</script>
<script type="text/html" id="usersTemplate">
    <ul>
        {{each(i, userName) userNames()}}
            <li><button data-bind="click: function() { userNames.remove(userName) }">Remove</button> <div>${ userName }</div></li>
        {{/each}}
</ul>
</script>

 

 

代碼: View model

 

 

// The view model holds all the state we're working with. It also has methods that can edit it, and it uses
// dependentObservables to compute more state in terms of the underlying data
// --
// The view (i.e., the HTML UI) binds to this using data-bind attributes, so it always stays up-to-date with
// the view model, even though the view model does not know or care about any view that binds to it

var viewModel = {
    savedLists: ko.observableArray([
        { name: "Celebrities", userNames: ['JohnCleese', 'MCHammer', 'StephenFry', 'algore', 'StevenSanderson'] },
        { name: "Microsoft people", userNames: ['BillGates', 'shanselman', 'haacked', 'ScottGu'] },
        { name: "Tech pundits", userNames: ['Scobleizer', 'LeoLaporte', 'techcrunch', 'BoingBoing', 'timoreilly', 'codinghorror'] }
    ]),

    editingList: {
        name: ko.observable("Tech pundits"),
        userNames: ko.observableArray()
    },

    userNameToAdd: ko.observable(""),
    currentTweets: ko.observableArray([])
};

viewModel.findSavedList = function (name) {
    var lists = this.savedLists();

    for (var i = 0; i < lists.length; i++)
        if (lists[i].name === name)
            return lists[i];
};

// Methods
viewModel.addUser = function () {
    if (this.userNameToAdd() && this.userNameToAddIsValid()) {
        this.editingList.userNames.push(this.userNameToAdd());
        this.userNameToAdd("");
    }
}

viewModel.saveChanges = function () {
    var saveAs = prompt("Save as", this.editingList.name());

    if (saveAs) {
        var dataToSave = this.editingList.userNames().slice(0);
        var existingSavedList = this.findSavedList(saveAs);
        if (existingSavedList)
            existingSavedList.userNames = dataToSave; // Overwrite existing list
        else
            this.savedLists.push({ name: saveAs, userNames: dataToSave }); // Add new list

        this.editingList.name(saveAs);
    }
}

viewModel.deleteList = function () {
    var nameToDelete = this.editingList.name();
    var savedListsExceptOneToDelete = $.grep(this.savedLists(), function (list) { return list.name != nameToDelete });
    this.editingList.name(savedListsExceptOneToDelete.length == 0 ? null : savedListsExceptOneToDelete[0].name);
    this.savedLists(savedListsExceptOneToDelete);
};

ko.dependentObservable(function () {
    // Observe viewModel.editingList.name(), so when it changes (i.e., user selects a different list) we know to copy the saved list into the editing list
    var savedList = viewModel.findSavedList(viewModel.editingList.name());

    if (savedList) {
        var userNamesCopy = savedList.userNames.slice(0);
        viewModel.editingList.userNames(userNamesCopy);
    } else
        viewModel.editingList.userNames([]);
});

viewModel.hasUnsavedChanges = ko.dependentObservable(function () {
    if (!this.editingList.name())
        return this.editingList.userNames().length > 0;

    var savedData = this.findSavedList(this.editingList.name()).userNames;
    var editingData = this.editingList.userNames();
    return savedData.join("|") != editingData.join("|");
}, viewModel);

viewModel.userNameToAddIsValid = ko.dependentObservable(function () {
    return (this.userNameToAdd() == "") || (this.userNameToAdd().match(/^\s*[a-zA-Z0-9_]{1,15}\s*$/) != null);
}, viewModel);

// The active user tweets are (asynchronously) computed from editingList.userNames
ko.dependentObservable(function () {
    twitterApi.getTweetsForUsers(this.editingList.userNames(), function (data) { viewModel.currentTweets(data) })
}, viewModel);

ko.applyBindings(viewModel);

// Using jQuery for Ajax loading indicator - nothing to do with Knockout
$(".loadingIndicator").ajaxStart(function () { $(this).fadeIn(); })
                      .ajaxComplete(function () { $(this).fadeOut(); });

 

Knockout應用開發指南 第十章:更多信息(完結篇)

2011-12-05 09:16 by 湯姆大叔, 7762 閱讀, 2 評論, 收藏, 編輯

1   瀏覽器支持

Knockout在以下瀏覽器經過測試:

Mozilla Firefox 2.0+(最新測試版本:3.6.8)

Google Chrome(經過Windows and Mac 下的version 5測試;其它低版本也該能夠工做)

Microsoft Internet Explorer 6, 7, 8

Apple Safari(Windows下的Safari 5測試,Mac OS X下的 Safari 3.1.2測試,以及iPhone下的Safari for iOS 4測試;高低版本應該均可以工做)

Opera 10 for Windows

Knockout應該在以上這個瀏覽器的各版本上工做,可是因爲太多版本,沒有逐一測試。最新測試結果顯示, Knockout在以下瀏覽器也是能夠工做的(儘管沒有對每一個版本逐一測試):

    Opera Mini

    Google Android OS browser (OS version 2.2)

測試Knockout可否在一個新平臺或瀏覽器下工做,只須要下載源代碼,而後在該瀏覽器裏運行裏面的/spec/runner.html文件便可測試。這個文件能夠驗證超過100個行爲特性,若是有問題則會生成報表。上述瀏覽器的測試結果都應該是100%經過。

2      尋求幫助

有任何問題,你能夠在Google group進去尋求幫助。

地址:http://groups.google.com/group/knockoutjs

3      更多教程和例子

這裏有更多使用Knockout和其它相關技術的頁面和例子:

    Knock Me Out — Ryan Niemeyer的博客,包括KnockoutJS和相關技術的不少好創意、想法和討論

    Editing a variable-length list, Knockout-style — Steve Sanderson展現的在ASP.NET MVC下使用Knockout的好處

    Knockout+WebSockets — Carl Hörberg使用Knockout,Sinatra,SQLite和WebSockets實現的實時討論版

    Knockout – quick asp.net mvc sample — Steve Gentile提供的另一篇博客, 關於ASP.NET MVC 下如何使用Knockout

    Log4Play: Log4j Live Streaming with Play Framework, Knockout.js and WebSockets — Felipe Oliveira關於使用KO和WebSockets建立實時server log功能

    Wiki – 攻略 — 網友貢獻的攻略和例子

    Wiki – 插件 — 網友貢獻的各類插件列表

 

此文列表正在收集中,若是你想讓你的帖子鏈接放在這裏,請告訴我。

用Javascript評估用戶輸入密碼的強度(Knockout版)

2011-11-27 10:40 by 湯姆大叔, 8090 閱讀, 10 評論, 收藏, 編輯

早上看到博友6點多發的一篇關於密碼強度的文章(鏈接),甚是感動(週末大早上還來發文)。

咱們來看看若是使用Knockout更簡單的來實現密碼強度的驗證。

原有代碼請查看:

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <script type="text/javascript">
        //CharMode函數
function CharMode(iN) {
            if (iN >=48&& iN <=57) //數字
return1;
            if (iN >=65&& iN <=90) //大寫字母
return2;
            if (iN >=97&& iN <=122) //小寫
return4;
            else
                return8; //特殊字符
        }

        //bitTotal函數
function bitTotal(num) {
            modes =0;
            for (i =0; i <4; i++) {
                if (num &1) modes++;
                num >>>=1;
            }
            return modes;
        }

        //checkStrong函數
function checkStrong(sPW) {
            if (sPW.length <=4)
                return0; //密碼過短
            Modes =0;
            for (i =0; i < sPW.length; i++) {
                Modes |= CharMode(sPW.charCodeAt(i));
            }
            return bitTotal(Modes);
        }


        //pwStrength函數
function pwStrength(pwd) {
            O_color ="#eeeeee";
            L_color ="#FF0000";
            M_color ="#FF9900";
            H_color ="#33CC00";
            if (pwd ==null|| pwd =='') {
                Lcolor = Mcolor = Hcolor = O_color;
            } else {
                S_level = checkStrong(pwd);
                switch (S_level) {
                    case0:
                        Lcolor = Mcolor = Hcolor = O_color;
                    case1:
                        Lcolor = L_color;
                        Mcolor = Hcolor = O_color;
                        break;
                    case2:
                        Lcolor = Mcolor = M_color;
                        Hcolor = O_color;
                        break;
                    default:
                        Lcolor = Mcolor = Hcolor = H_color;
                }

                document.getElementById("strength_L").style.background = Lcolor;
                document.getElementById("strength_M").style.background = Mcolor;
                document.getElementById("strength_H").style.background = Hcolor;
                return;
            }
        } </script>
    <form name="form1" action="">
    輸入密碼:<input type="password" size="10" onkeyup="pwStrength(this.value)" onblur="pwStrength(this.value)">
    <br>
    密碼強度:
    <table width="217" border="1" cellspacing="0" cellpadding="1" bordercolor="#cccccc"
        height="23" style='display: inline'>
        <tr align="center" bgcolor="#eeeeee">
            <td width="33%" id="strength_L">
                弱
            </td>
            <td width="33%" id="strength_M">
                中
            </td>
            <td width="33%" id="strength_H">
                強
            </td>
        </tr>
    </table>
    </form>
</body>
</html>

 

 

首先咱們來改善一下上面博友的驗證函數爲以下代碼:

var Page = Page || {};
Page.Utility = Page.Utility || {};
Page.Utility.Registration = Page.Utility.Registration || {};

//獲取密碼強度
Page.Utility.Registration.getPasswordLevel = function (password) {
    if (password == null || password == '')
        return 0;

    if (password.length <= 4)
        return 0; //密碼過短

    var Modes = 0;
    for (i = 0; i < password.length; i++) {
        Modes |= CharMode(password.charCodeAt(i));
    }
    return bitTotal(Modes);

    //CharMode函數
    function CharMode(iN) {
        if (iN >= 48 && iN <= 57) //數字
            return 1;
        if (iN >= 65 && iN <= 90) //大寫字母
            return 2;
        if (iN >= 97 && iN <= 122) //小寫
            return 4;
        else
            return 8; //特殊字符
    }

    //bitTotal函數
    function bitTotal(num) {
        modes = 0;
        for (i = 0; i < 4; i++) {
            if (num & 1) modes++;
            num >>>= 1;
        }
        return modes;
    }
};

 

而後來建立View Model,可是引用Knockout以前,咱們首先要引用Knockout的Js類庫(具體介紹請查看Knockout應用開發指南的系列教程)
View model代碼以下:

var viewModel = {
    Password: ko.observable(""),
    Ocolor: "#eeeeee"
};

對於密碼強度以及顏色的值依賴於密碼字符串的值,因此咱們須要爲他們聲明依賴屬性,代碼以下:

viewModel.PasswordLevel = ko.dependentObservable(function () {
    return Page.Utility.Registration.getPasswordLevel(this.Password());
}, viewModel);

viewModel.Lcolor = ko.dependentObservable(function () {
    //根據密碼強度判斷第一個格顯示的背景色
    return this.PasswordLevel() == 0 ? this.Ocolor : (this.PasswordLevel() == 1 ? "#FF0000" : (this.PasswordLevel() == 2 ? "#FF9900" : "#33CC00"))
}, viewModel);

viewModel.Mcolor = ko.dependentObservable(function () {
    //根據密碼強度判斷第二個格顯示的背景色
    return this.PasswordLevel() < 2 ? this.Ocolor : (this.PasswordLevel() == 2 ? "#FF9900" : "#33CC00")
}, viewModel);

viewModel.Hcolor = ko.dependentObservable(function () {
    //根據密碼強度判斷第三個格顯示的背景色
    return this.PasswordLevel() < 3 ? this.Ocolor : "#33CC00"
}, viewModel);

而後使用applyBindings方法將view model綁定到該頁面,你可使用jQuery的ready函數來執行該綁定代碼,也能夠在頁面最下方執行綁定代碼,咱們這裏使用了jQuery,代碼以下:

$((function () {
    ko.applyBindings(viewModel);
}));

 

最後,咱們再看看這些值怎麼動態綁定到HTML元素上的,請查看以下代碼(其中使用了afterkeydown代替了onKeyUp和onBlur):

<form name="form1" action="">
輸入密碼:
<input type="text" size="10" data-bind="value:Password, valueUpdate: 'afterkeydown'">
<br>
密碼強度:
<table width="217" border="1" cellspacing="0" cellpadding="1" bordercolor="#cccccc"
    height="23" style='display: inline'>
    <tr align="center" bgcolor="#eeeeee">
        <td width="50"data-bind="style: { backgroundColor: Lcolor }">弱</td>
        <td width="50"data-bind="style: { backgroundColor: Mcolor }">中</td>
        <td width="50"data-bind="style: { backgroundColor: Hcolor }">強</td>
    </tr>
</table>
</form>

而後就OK,運行代碼查看,如出一轍的功能展現出來了。

若是去掉爲驗證而改善的代碼,總代碼確定是比原有的方式少的。

 

完整版代碼以下:

 

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head>     <script type="text/javascript" src="http://knockoutjs.com/js/jquery-1.4.2.min.js"></script>     <script type="text/javascript" src="http://knockoutjs.com/js/jquery.tmpl.js"></script>     <script type="text/javascript" src="http://knockoutjs.com/js/knockout-1.2.1.js"></script> </head> <body>     <script type="text/javascript">         var Page = Page || {};         Page.Utility = Page.Utility || {};         Page.Utility.Registration = Page.Utility.Registration || {};         //獲取密碼強度         Page.Utility.Registration.getPasswordLevel =function (password) {             if (password ==null|| password =='')                 return0;             if (password.length <=4)                 return0; //密碼過短             var Modes =0;             for (i =0; i < password.length; i++) {                 Modes |= CharMode(password.charCodeAt(i));             }             return bitTotal(Modes);             //CharMode函數 function CharMode(iN) {                 if (iN >=48&& iN <=57) //數字 return1;                 if (iN >=65&& iN <=90) //大寫字母 return2;                 if (iN >=97&& iN <=122) //小寫 return4;                 else                     return8; //特殊字符             }             //bitTotal函數 function bitTotal(num) {                 modes =0;                 for (i =0; i <4; i++) {                     if (num &1) modes++;                     num >>>=1;                 }                 return modes;             }         };         var viewModel = {             Password: ko.observable(""),             Ocolor: "#eeeeee"         };         viewModel.PasswordLevel = ko.dependentObservable(function () {             return Page.Utility.Registration.getPasswordLevel(this.Password());         }, viewModel);         viewModel.Lcolor = ko.dependentObservable(function () {             //根據密碼強度判斷第一個格顯示的背景色 returnthis.PasswordLevel() ==0?this.Ocolor : (this.PasswordLevel() ==1?"#FF0000" : (this.PasswordLevel() ==2?"#FF9900" : "#33CC00"))         }, viewModel);         viewModel.Mcolor = ko.dependentObservable(function () {             //根據密碼強度判斷第二個格顯示的背景色 returnthis.PasswordLevel() <2?this.Ocolor : (this.PasswordLevel() ==2?"#FF9900" : "#33CC00")         }, viewModel);         viewModel.Hcolor = ko.dependentObservable(function () {             //根據密碼強度判斷第二個格顯示的背景色 returnthis.PasswordLevel() <3?this.Ocolor : "#33CC00"         }, viewModel);         $((function () {             ko.applyBindings(viewModel);         }));            </script>     <form name="form1" action="">     輸入密碼:<input type="text" size="10" data-bind="value:Password, valueUpdate: 'afterkeydown'">     <br>     密碼強度:     <table width="217" border="1" cellspacing="0" cellpadding="1" bordercolor="#cccccc"         height="23" style='display: inline'>         <tr align="center" bgcolor="#eeeeee">             <td width="50" id="strength_L" data-bind="style: { backgroundColor: Lcolor }">                 弱             </td>             <td width="50" id="strength_M" data-bind="style: { backgroundColor: Mcolor }">                 中             </td>             <td width="50" id="strength_H" data-bind="style: { backgroundColor: Hcolor }">                 強             </td>         </tr>     </table>     </form> </body> </html>

相關文章
相關標籤/搜索