Knockout基礎(三)

上一篇最後講到了select的操作,這一篇要來為頁面增加更多功能

首先來新增顧客,上一篇有稍微提到,新增顧客與刪除顧客都是對顧客陣列(self.orders)的操作

要新增客人,就是對顧客陣列新增一個物件,所以程式碼如下:

ViewModel

//新增點餐
self.addOrder = function () {
    self.orders.push(new OrderMeal("", self.chooseMeals[0]))
}

View

<button data-bind="click: addOrder,enable: orders().length<10">新增餐點</button>

就如同上面所說,要新增顧客,只要對顧客陣列新增一個物件即可

所以對顧客陣列使用push方法,而預設號碼為空值、預設餐點為套餐一

而data-bind的參數使用click,代表點擊按鈕時要執行什麼function,而本例是設定addOrder方法,所以執行畫面如下:點擊按鈕更改號碼,等待取餐區也會跟著變更

 

再來刪除顧客,只要將顧客陣列移除指定的顧客即可,程式碼如下:

ViewModel

//刪除餐點
self.removeOrder = function (order) {
    self.orders.remove(order);  
}

View

<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>

要刪除陣列元素所使用的方法為remove,但該方法需要傳入一個參數,就是要刪除的陣列元素

所以在removeOrder方法裡我設定了一個參數order,這個會是要刪除的元素資料

接下來在頁面設定一個按鈕用來綁定刪除功能

跟上一篇講解的一樣,因為按鈕的位置是在foreach的model物件裡面

所以要使用ViewModel的方法就要加上$root

會寫在foreach裡是因為希望每一個顧客資料(model)都有一個刪除按鈕

那又為什麼不乾脆寫在model裡面呢?

那是因為顧客資料陣列是在ViewModel,新增或刪除顧客都是對ViewModel裡的顧客陣列去作修改,所以方法也要寫在ViewModel裡

綁定完之後按下刪除鈕時,KO便會偵測按下去的是哪一個物件的刪除鈕

並把該物件的資料傳給removeOrder方法,操作結果如下:

初始畫面先新增兩筆按鈕再按下號碼的刪除鈕如此一來便可以刪除資料啦

 

接下來要介紹一些很常使用到的data-bind裡的參數,首先先在按鈕的data-bind與按鈕下方加入以下程式碼:

<button data-bind="click: addOrder,enable: orders().length<10">新增餐點</button>
<br>
<p>
    目前排隊人數 : <span data-bind="text: orders().length"></span>
    <span data-bind="text: '(排隊人數已達上限,請稍後再來)',visible: orders().length>=10"></span>
</p>

顧客陣列(orders)既然是一個陣列,那也能用陣列的方法與屬性來操作

orders().length是顧客陣列長度,同時代表著顧客人數,所以可以用該屬性來表示目前人數

 

再來是按鈕的新參數:enable,該參數代表在符合後面的條件下是可以使用該元件的

此處代表若顧客人數小於10,就可以使用按鈕來新增顧客

同時也代表著顧客人數上限為10個,因為陣列元素數量為10時,該按鈕就失去功效無法新增顧客了

第三個參數為visible,該參數代表符合後面的條件時才能看到該元件,此處是當顧客數量大於等於10時才會顯示該元件

所以綜合結果如下:

初始畫面,顯示目前人數增加到10人當陣列人數達到10人時,按鈕便失去功用而反白,原本隱藏的文字就會顯示出來。透過設定enable和visible,可以對元件作更加多元的設定

 

最後來點簡單的小設定吧。我們來顯示總金額為多少,程式碼如下:

ViewModel

//總待收金額
self.totalPrice = ko.computed(function () {
    var price = 0;
    self.orders().forEach(function (item) {
        price += item.meal().price;
    })
    return price;
})

View

<p><span data-bind="text: '應收取總費用:' + totalPrice() + '元'"></span></p>


利用forEach方法去讀取顧客陣列裡,每一個物件的餐點價格,並全部加起來

在View方面其實也可以直接在text參數的部分直接拼接出你想要顯示的句子。最終畫面顯示如下:

 

KO的基礎到這邊結束,而其他還有很多好用的方法,可以讓畫面的綁定操作更多元,後面有時間的話會再補上來。

以下為完整程式碼:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="Scripts/knockout-3.5.1.debug.js"></script>
</head>
<body>

    <button data-bind="click: addOrder,enable: orders().length<10">新增餐點</button>
    <br>
    <p>
        目前排隊人數 : <span data-bind="text: orders().length"></span>
        <span data-bind="text: '(排隊人數已達上限,請稍後再來)',visible: orders().length>=10"></span>
    </p>

    <br>

    <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>
    
    <br>
    <p><span data-bind="text: '應收取總費用:' + totalPrice() + '元'"></span></p>
    <br>
    <br>
    <br>

    <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>

    <script>

        //顧客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 + " 元";
            })

        }

        //viewModel
        function OrderViewModel() {

            var self = this;

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

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

            //新增點餐
            self.addOrder = function () {
                self.orders.push(new OrderMeal("", self.chooseMeals[0]))
            }

            //刪除餐點
            self.removeOrder = function (order) {
                self.orders.remove(order);  
            }

            //總待收金額
            self.totalPrice = ko.computed(function () {
                var price = 0;
                self.orders().forEach(function (item) {
                    price += item.meal().price;
                })
                return price;
            })

        }

        ko.applyBindings(new OrderViewModel());
    </script>
</body>
</html>