关闭

knockout.js实例二~购物车

标签: knockoutjs
813人阅读 评论(0) 收藏 举报
分类:

knockout.js实例二~购物车

本实例完成了一个基本的购物车功能,涉及到knockout.js中的知识点包含 select 标签的绑定, select 标签的级联, with 绑定,数据格式化, 依赖属性以及模板绑定等, 先上代码:

ViewModel Code:

var categories = [
    {
        Name: '服饰',
        Products:[
            {Name: '七分裤', Price:49.9},
            {Name: '休闲裤', Price:59.9},
            {Name: '卫衣', Price:69.9}
        ]
    },
    {
        Name: '生活用品',
        Products: [
            {Name: '海飞丝', Price:9.9},
            {Name: '霸王', Price:19.9},
            {Name: '潘婷', Price:29.9}
        ]
    },
    {
        Name: '饮料',
        Products: [
            {Name: '百事可乐', Price:3.9},
            {Name: '脉动', Price:4.9},
            {Name: '乐虎', Price:5.9}
        ]
    }
];

function cartModel(){
    var self = this;
    self.category = ko.observable();
    self.product = ko.observable();
    self.quantity = ko.observable(1);
    self.subtotal = ko.computed(
        function () {
            return self.product() ? self.product().Price * self.quantity() : 0;
        },
        self
    );
}


function cartsViewModel(){
    var self = this;

    self.categories = ko.observableArray(categories);

    self.carts = ko.observableArray([new cartModel()]);

    self.addProduct = function () {
        self.carts.push(new cartModel());
    };

    self.removeProduct = function (product) {
        self.carts.remove(product);
    };


    self.totalPrice = ko.computed(
        function () {
            var total = 0;

            $.each(self.carts(),
                function (index, item) {
                    total += item.subtotal();
                }
            );

            return total;
        },
        self
    );

    self.commitCart = function () {
        var dataToSave = $.map(self.carts(),
            function(cart) {
            return cart.product() ? {
                product: cart.product().Name,
                quantity: cart.quantity()
            }: undefined
        });
        alert(ko.toJSON(dataToSave));
    };

    self.moneyFormat = function (value){
        return '$' + value.toFixed(2);
    };


}

ko.applyBindings(new cartsViewModel());

Html Code:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Carts Editor</title>
    <link rel="stylesheet" href="../css/bootstrap.min.css"/>
</head>
<body>

    <script type="text/html" id="cartsTemplate">
        <tr>
            <td>
                <select class="form-control" data-bind="options: categories, optionsText: 'Name', optionsCaption:'选择类别...', value: category"></select>
            </td>
            <td data-bind="with: category">
                <select class="form-control" data-bind="options: Products, optionsText: 'Name', optionsCaption:'选择商品...',  value: $parent.product"></select>
            </td>
            <td>
                <span data-bind="visible: product, text: $parent.moneyFormat(product() ? product().Price : 0)"></span>
            </td>
            <td>
                <input class="form-control" type="text" data-bind="visible: product, value: quantity, valueUpdate: 'afterkeydown'"/>
            </td>
            <td>
                <span data-bind="visible: product, text: $parent.moneyFormat(subtotal())"></span>
            </td>
            <td>
                <a href="#" class="btn btn-danger btn-sm" data-bind="click: $parent.removeProduct">移除</a>
            </td>
        </tr>
    </script>
    <hr/>

    <div class="container">
        <div class="row">
            <a href="#" class="btn btn-primary btn-sm" data-bind="click: addProduct">新增</a>&nbsp;
            <a href="#" class="btn btn-primary btn-sm" data-bind="click: commitCart">提交</a>
            <span class="col-md-2 pull-right" data-bind="text: '总计:' + moneyFormat(totalPrice())"></span>
            <table class="table table-hover table-bordered table-responsive">
                <thead>
                    <tr>
                        <th class="col-md-2">类别</th>
                        <th class="col-md-2">名称</th>
                        <th class="col-md-2">单价</th>
                        <th class="col-md-2">数量</th>
                        <th class="col-md-2">小计</th>
                        <th class="col-md-2">操作</th>
                    </tr>
                </thead>
                <tbody data-bind="template:{name: 'cartsTemplate', foreach: carts}">
                </tbody>
            </table>
        </div>
    </div>

    <script type="text/javascript" src="../scripts/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="../scripts/knockout-3.3.0.js"></script>
    <script type="text/javascript" src="../viewModels/cartsViewModel.js"></script>
</body>
</html>

首先我在ViewModel中为了模拟数据, 使用了静态的 categories 来作为基本数据, 它的结构很清晰, Name 表示类型名称,
Products 表示与类型所对应的商品, 以及里面的 Name 表示商品名称, Price 表示商品单价。 接下来我们需要构建一个cartModel 来表示我们单个选择好的商品, 它需要具备以下几个属性: 商品类别, 商品(包含名称与价格),数量, 小计价格。 注意在此小计价格是由商品单价以及数量所计算出来的, 所以小计价格是依赖于商品与数量的,所以他是一个依赖属性, 对于依赖属性, 我们使用ko.computed来声明 ko.computed 的签名为 ko.computed(func, target), func 是一个返回此小计属性的值得函数, 在javascript中, 函数并不属于某个具体的对象, 只是在调用的时候被某个对象所调用, 也就是来设定其作用域, target 即表示需要将此函数的作用域设置为 target的值。

下面我们继续构建一个cartsViewModel 作为我们需要绑定到 html 页面的 viewmodel, 在这个模型中,我们首先需要将商品类型作为我们绑定商品类型 select 的数据源 ,即

self.categories = ko.observableArray(categories); 

接着我们需要定义一个 cartModel 的数组用于绑定我们页面上的购物车列表, 即

self.carts = ko.observableArray([new cartModel()]); 

还有对于商品的增加和移除,分别为:

self.addProduct = function () {
    self.carts.push(new cartModel());
};

self.removeProduct = function (product) {
    self.carts.remove(product);
};

对于商品的新增,我们只需要往 self.carts对象中push一个空的 cartModel就行, 对于移除,只需将传入的product 从 self.carts中移除就行, 因为我们的 self.carts 是一个监控对象, 且绑定了 html 页面的购物车列表模板, 所以当我们的 self.carts 产生变化的时候, ui界面也会产生相对应的变化。

下面我们需要计算所选上面总价,以及向我们的远程服务器提交我们的数据:

self.totalPrice = ko.computed(
    function () {
        var total = 0;
        $.each(self.carts(),
            function (index, item) {
                total += item.subtotal();
            }
        );
        return total;
    },
    self
);

self.commitCart = function () {
    var dataToSave = $.map(self.carts(),
        function(cart) {
        return cart.product() ? {
            product: cart.product().Name,
            quantity: cart.quantity()
        }: undefined
    });
    alert(ko.toJSON(dataToSave));
};

首先我们看 self.totalPrice , 此函数遍历了我们的 self.carts , 然后将每一项产品的小计价格进行相加, 得到总价, 较为好理解。 self.commitCart 函数对我们的 self.carts 进行映射, 获取每一项商品的名称与所选数量, 映射到一个新的 数组中, 然后同我们 ko.toJSON 函数来讲这个数组转化为Json数据。

注意一点就是: 在上面的映射过程中,我们是取到了商品的名称, 但是实际开发过程中, 由于我们需要获取与标识符所对应的唯一商品, 所以一般我们会在初始化数据中加入可以标示商品唯一的Id, 然后在映射新的数组已用来提交时会映射出这个唯一的标志Id。

对于单价,小计,总计,我们需要对其进行格式化处理:

self.moneyFormat = function (value){
    return '$' + value.toFixed(2);
};

toFixed 函数保证了其保留两位小数, 前面的美元符号依情况而定。

下面不要忘记我们最重要的一部操作,应用绑定:

ko.applyBindings(new cartsViewModel());

好,下面我们接着来看html code :

首先我们看上面用到的购物车模板:

<script type="text/html" id="cartsTemplate">
    <tr>
        <td>
            <select class="form-control" data-bind="options: categories, optionsText: 'Name', optionsCaption:'选择类别...', value: category"></select>
        </td>
        <td data-bind="with: category">
            <select class="form-control" data-bind="options: Products, optionsText: 'Name', optionsCaption:'选择商品...',  value: $parent.product"></select>
        </td>
        <td>
            <span data-bind="visible: product, text: $parent.moneyFormat(product() ? product().Price : 0)"></span>
        </td>
        <td>
            <input class="form-control" type="text" data-bind="visible: product, value: quantity, valueUpdate: 'afterkeydown'"/>
        </td>
        <td>
            <span data-bind="visible: product, text: $parent.moneyFormat(subtotal())"></span>
        </td>
        <td>
            <a href="#" class="btn btn-danger btn-sm" data-bind="click: $parent.removeProduct">移除</a>
        </td>
    </tr>
</script>
  • 商品类别:

options: 绑定了我们需要展示的类别数据源, 即 self.categories

optionsText: 绑定了类别的显示字段, 如果有需要的话, 会使用 optionsValue 来绑定值字段

optionsCaption: 绑定了类别的初始化显示提示

value: 绑定了所选类别的值

  • 商品:

注意一下, 我们在这里使用了 with 绑定, 这个绑定表示可以在此所属标签的下级标签中访问绑定值的子属性字段, 由于我们需要选择的商品初始化数据包含在 categories 中, 所以当我们选择了某一个 category 之后, 我们可以通过它的子属性 Products 来访问与此类型关联的商品数据, 所以我们可以看到在这里我们的 options 绑定的是Products, 后续两项我就不做介绍了, 跳过看 value 绑定, value 绑定了我们单个购物车项的 product, 注意我们这里的 parent,使parent 呢? 是因为当前作用域是在 with: category 中的, 如果我们需要访问 product, 则需要往上一次进行查找。

  • 单价:

绑定了 cartModel 里面的所选商品对象 product 的子属性 Price, 所以值为 product().Price

  • 数量:

绑定了 cartModel 里面的所选商品数量 quantity, 设置 valueUpdate: ‘afterkeydown’ 是为了在用户按下键之后立刻更新商品数量数据

  • 小计:

绑定了 cartModel 里面的所选商品数量 subtotal

注意:在我们的单价, 数量, 小计中,我们绑定了 visible: product, 这是为了当 product 没有选择的情况下,是不需要用户看到的。

模板定义好了,下面就需要对模板的绑定:

<tbody data-bind="template:{name: 'cartsTemplate', foreach: carts}">
</tbody>

剩下的就是对新增,提交,以及总价的绑定了,较为简单,具有 knockout.js基本知识的就可以理解了。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:16470次
    • 积分:346
    • 等级:
    • 排名:千里之外
    • 原创:16篇
    • 转载:1篇
    • 译文:0篇
    • 评论:6条
    最新评论