Knockout基礎(二)

第二篇要來講解Model的部分了,本篇會以去早餐店點餐為例。

Model可以當作就是物件導向裡的物件,建立好一個Model樣板後就可以建立許多該物件

以早餐店為例,每一個顧客就是一個Model,每個顧客會有三個屬性:號碼牌的號碼、餐點、價格

所以我們先寫一個Model出來:

//顧客model
function OrderMeal(number, meal) {

    var self = this;
    self.number = ko.observable(number);  //號碼
    self.meal = ko.observable(meal);  //餐點

    //顯示費用
    self.price = ko.computed(function () {
        return "新台幣 " + self.meal().price + " 元";
    })

}

在建立每個物件時,我需要傳入兩個參數:號碼與餐點

而傳入的參數會帶進屬性裡面,因號碼與餐點還有可能會變動

所以用ko.observable來觀察該屬性,而後面的價格我們稍會再談

 

Model處理好後,下一步就是建立菜單,菜單部分程式碼如下:

//可選擇餐點的model 想成來自資料庫的資源 不能更動
self.chooseMeals = [
    { mealName: "套餐一(豬排蛋堡 + 大杯飲料)", price: 40 },
    { mealName: "套餐二(黑胡椒鐵板麵 + 蛋 + 大冰紅)", price: 55 },
    { mealName: "套餐三(咔拉雞腿堡 + 厚片系列 + 大杯飲料)", price: 80 },
    { mealName: "兒童餐(脆薯 + 麥克雞塊 + 兒童漢堡 + 大杯飲料)", price: 100 }
]

菜單陣列裡會有多個物件,每個物件就是一個套餐的資訊,會包含套餐名稱與價格

而在實際應用時會用Ajax或其他方法將要顯示的內容載入,所以這部分通常不會變動,也就不用去觀察它

 

再來就是本篇的重點了,我們要建立一個已點餐的顧客陣列,程式碼如下:

//已點餐的顧客
self.orders = ko.observableArray([
    new OrderMeal("1", self.chooseMeals[1]),
    new OrderMeal("2", self.chooseMeals[2])
])

首先,一個顧客就是一個物件(Model)

所以要儲存所有顧客的資料,就得建立一個陣列

而KO觀察陣列的方法是ko.observableArray

使用此方法後,若陣列有所變動(增加物件---新增顧客;減少物件---刪除顧客,等等會提到應用)KO就會立即反映到頁面上

一開始我們先預設建立兩個物件,分別傳入號碼與餐點,self.chooseMeals[1]就是套餐二、self.chooseMeals[2]為套餐三

以此類推。再回到Model的程式碼,其中一段為:

//顯示費用
self.price = ko.computed(function () {
    return "新台幣 " + self.meal().price + " 元";
})

費用的部分使用了一個方法為ko.computed,可以把這個方法想成對某個屬性加工

以本例來說,self.meal()代表的是該顧客所點的餐點

而餐點裡面有餐點名稱(mealName)和價錢(price)

所以我想要取得餐點價格的話就要用self.meal().price來取得價錢

而meal要加括號的原因,是因為self.meal這個變數我已經使用ko.observable這個方法來觀察

所以想要讀取該方法的值的話就需要加括號

取得了價錢之後,我在前後加上想要的字串,這樣我若想讀取model的price屬性

就會顯示return的結果,而且如果self.meal()有變動,ko.computed會自動偵測並跟著變動

 

以上建立好之後,就可以開始對view的部分進行撰寫,程式碼如下:

<strong>等待取餐</strong>
<table border="1" style="border-style:dashed;">
    <thead>
        <tr>
            <th>號碼</th>
            <th>餐點</th>
            <th>費用</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: orders">
        <tr>
            <td><span data-bind="text: number"></span></td>
            <td><span data-bind="text: meal().mealName"></span></td>
            <td><span data-bind="text: meal().price"></span></td>
        </tr>
    </tbody>
</table>

顯示結果為:在tbody的部分加上data-bind=”foreach:orders”,KO會利用迴圈的方式

將顧客陣列(orders)的每一個顧客資料(model)顯示出來

而想要顯示什麼內容就設定在tbody標籤內,裡面我設定顯示號碼、餐點與價格的部分

 

若要修改每一個顧客的資料要怎麼處理呢?首先頁面的程式碼如下:

<strong>設定取餐</strong>
<table border="1" style="border-style:dashed;">
    <thead>
        <tr>
            <th>號碼</th>
            <th>餐點</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: orders">
        <tr>
            <td><input data-bind="value: number"></td>
            <td><select data-bind="options: $root.chooseMeals, value:meal, optionsText: 'mealName'"></select></td>
            <td><button data-bind="click: $root.removeOrder">刪除</button></td>
        </tr>
    </tbody>
</table>

顯示結果為:大部分前面都有提過,而這裡要講解的是select的操作

在select裡的data-bind有三個參數,第一個是options

將你要顯示的選單內容放到該位置,本例是放置chooseMeals

但前面必須要加上$root,因為我們目前的位置是在foreach裡的model物件

這個物件並沒有chooseMeals,若我們想要往上一層到ViewModel就必須要加上$root

第二個value就是選單的值要帶入的變數名稱

當我們選擇了一個選項,就會取得該選項的值,而該值就會帶到model裡面的meal變數

反過來說,當我們設定了meal變數的值,選單就會自動對應到該選項

所以顯示結果的畫面是頁面一載入時,選單自動選擇的選項

第三個optionsText則是要顯示的選項名稱,而這裡是設定為chooseMeals裡每一個物件的mealName

 

讓我們來點選一下選單點選兒童餐後
當點選了兒童餐後,後面的價格也跟著變動了 

另外補充,若你想存入的是單一個值而不是物件,則可以加入第四個參數:optionsValue

該參數可以設定選項要存入什麼值

由於本例沒有使用該參數,KO會直接將選項的值設為整個餐點物件

若你只想要取得餐點的價格,可以設定為optionsValue:'price'

系統會讀取每個套餐物件的price值並設定為該選項的值,當然相關的程式碼也要跟著作調整,完整data-bind參數為:

options: $root.chooseMeals, value:meal, optionsText: 'mealName',optionsValue:'price'   

                                                                                                             

由於篇幅過多,後續內容會擺到第三篇來講。