knockoutのcomputed property

Knockout Advent Calendar 2015

by MKGaru

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-debug.js"></script>
<script src="https://mbest.github.io/knockout.punches/knockout.punches.js"></script>
<script src="https://rawgit.com/SteveSanderson/knockout-es5/master/dist/knockout-es5.js"></script>
<h1>Fruit Shopping Order</h1>
<div>商品リスト:
    <table>
        <thead>
            <tr>
                <th>商品名</th>
                <th>単価</th>
                <th>注文</th>
            </tr>
        </thead>
        <tbody data-bind="foreach:items">
            <tr>
                <td>{{name}}</td>
                <td>{{price}}</td>
                <td>
                    <button type="button" data-bind="click:$parent.order.add">注文</button>
                </td>
            </tr>
        </tbody>
    </table>
</div>
<br>
<div>注文票:
    <table data-bind="with:order">
        <thead>
            <tr>
                <th>商品名</th>
                <th>単価</th>
                <th>数量</th>
                <th>小計</th>
                <th></th>
            </tr>
        </thead>
        <tbody data-bind="foreach:details">
            <tr>
                <td>{{item.name}}</td>
                <td>{{item.price}}</td>
                <td><input type="number" data-bind="value:quantity" min="1" style="width:3em;" /></td>
                <td>{{price}}</td>
                <td><button type="button" data-bind="on.click:$parent.details.splice($index(),1)">キャンセル</button></td>
            </tr>
        </tbody>
        <tbody data-bind="if:!details.length">
            <tr>
                <td colspan="5" style="text-align:center">Empty</td>
            </tr>
        </tbody>
        <tfooter>
            <tr>
                <td colspan="3">合計</td>
                <td>{{totalPrice}}</td>
            </tr>
        </tfooter>
    </table>
</div>

CSS

td,input{
    text-align:right;
}

JavaScript

function Item(name,price){
    this.name = name;
    this.price = price;
    ko.track(this);
}
function Shop(){
    this.items = [
        new Item("apple",60),
        new Item("banana",25),
        new Item("cinnamon",80),
        new Item("dragonfruit",120)
    ];
    this.order = new Order();
    ko.track(this);
}
function OrderDetail(item,quantity){
    this.item=item;
    this.quantity=quantity;
    ko.track(this);
    Object.defineProperty(this,'price',{get:function(){
        return this.item.price * this.quantity
    }});
}
function Order(){
    this.details = [];
    ko.track(this);
    Object.defineProperty(this,'totalPrice',{get:function(){
        var total = 0;
        this.details.forEach(function(detail){
            total += detail.price;
        });
        return total;
    }});
}
Order.prototype.add=function(item,event){
    var quantity = 1;
    this.details.push(new OrderDetail(item,quantity));
}

var vm = new Shop();

ko.punches.enableAll();
ko.applyBindings(vm);