Knockout.js + Bootstrap - 发生了奇怪的事情

时间:2014-07-10 13:22:56

标签: javascript twitter-bootstrap knockout.js

我使用Twitter Bootstrap和knockout.js。

我有一个订单页面,其中收银员可以选择客户想要的客户产品购买。但是,我得到了一些非常奇怪的行为。当我将一个产品添加到购物车时,会调用正确的函数addToCart,但是在没有告诉程序调用它的情况下调用函数removeFromCart。我猜有些事情正在发生,因为我使用 Bootstrap模态

请帮忙。这是小提琴http://jsfiddle.net/rY59d/4/

HTML代码:

<div id="create-order-main">
    <h2>Create new order</h2>
    <a class="btn btn-primary" data-toggle="modal" data-target="#select-products2"><b>+</b> Add products</a>
    <div>

    <div id="create-order-select-products" data-bind="with: productVM">
        <div class="modal fade" id="select-products2" tabindex="-1" role="dialog" aria-labelledby="selectProducts2Label" aria-hidden="true">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">
                            &times;
                        </button>
                        <h4 class="modal-title">Add products</h4>
                    </div>
                    <div class="modal-body">

                            <table class="table table-bordered table-with-records" data-bind="triggerUpdate: Products, visible: Products().length > 0">
                                <thead>
                                    <tr>
                                        <th>Id</th>
                                        <th>Name</th>
                                        <th>Price</th>
                                        <th>Quantity</th>
                                        <th></th>
                                    </tr>
                                </thead>
                                <tbody data-bind="foreach: filteredProducts2">
                                    <tr>
                                        <td data-bind="text: id"></td>
                                        <td data-bind="text: name"></td>
                                        <td data-bind="text: price"></td>
                                        <td><input type="number" min="0" step="1" value="1"></td>
                                        <td data-bind="attr: { value: $index }, click: $parent.selectedProduct2"><a class="btn btn-primary" data-bind="click: $parent.addToCart">Add to cart</a></td>

                                    </tr>
                                </tbody>
                            </table>

                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">
                            Cancel
                        </button>
                        <button type="submit" class="btn btn-primary" data-dismiss="modal">
                            Choose
                        </button>
                        </form>

                    </div>
                </div>
                <!-- /.modal-content -->
            </div>
            <!-- /.modal-dialog -->
        </div>
        <!-- /.modal -->

    </div>


    <div data-bind="with: productVM">
  <table class="table table-bordered table-with-records" data-bind="triggerUpdate: cart, visible: cart().length > 0">
                                <thead>
                                    <tr>
                                        <th>Id</th>
                                        <th>Name</th>
                                        <th>Price</th>
                                        <th></th>
                                    </tr>
                                </thead>
                                <tbody data-bind="foreach: cart">
                                    <tr>
                                        <td data-bind="text: id"></td>
                                        <td data-bind="text: name"></td>
                                        <td data-bind="text: price"></td>
                                        <td data-bind="attr: { value: $index }, click: $parent.selectedProductInOrder"><a class="btn btn-primary" data-bind="click: $parent.removeFromCart($data)">Remove</a></td>
                                    </tr>
                                </tbody>
                            </table>
    </div>

</div>

JavaScript代码:

/**
 * -----------
 * Viewmodels.js
 * -----------
 *
 * Contains Knockout.js Viewmodels
 *
 */

// CustomerViewModel starts here

var CustomerViewModel = function () {

    var self = this;

    var stringStartsWith = function (string, startsWith) {
        string = string || "";
        if (startsWith.length > string.length)
            return false;
        return string.substring(0, startsWith.length) === startsWith;
    };

    self.name = ko.observable("");
    self.surname = ko.observable("");
    self.email = ko.observable("");
    self.query = ko.observable();
    self.Customers = ko.observableArray();

    self.filterId = ko.observable("");
    self.filterName = ko.observable("");
    self.filterSurname = ko.observable("");

    // Used for search in "Create Order" view

    self.filterId2 = ko.observable("");
    self.filterName2 = ko.observable("");
    self.filterSurname2 = ko.observable("");

    function Customer(id, name, surname, email) {

        this.id = id;
        this.name = name;
        this.surname = surname;
        this.email = email;

    }

    self.selectedCustomer = ko.observable(null);

    // Used for search in "Create Order" view

    self.selectedCustomer2 = ko.observable(null);



    self.getId = function () {

        var idCounter;

        if (self.Customers().length === 0) {
            idCounter = 0;
        } else {
            idCounter = self.Customers()[self.Customers().length - 1]['id'];
        }
        return (++idCounter);
    };

    $.getJSON("api/customers", function (data) {
        self.Customers(data);
    });

    self.Customers.push(new Customer(1,"John","Smith","john@smith.com"));
    self.Customers.push(new Customer(2,"Maria","Jones","maria@jones.com"));
    self.Customers.push(new Customer(3,"Alexander","Stevenson","alexander@stevenson.com"));


    self.clearSearchCustomers = function () {
        self.filterId("");
        self.filterName("");
        self.filterSurname("");
    };

    // Used in the "Create new Order" view

    self.clearSearchCustomers2 = function () {
        self.filterId2("");
        self.filterName2("");
        self.filterSurname2("");
        self.selectedCustomer2("");

    };

    self.selectCustomer = function () {
        self.selectedCustomer(this);
    };

    self.chooseCustomerInSearch = function () {
        $('#select-customer2').modal('toggle');
    };

    self.createNewCustomer = function () {

        var customer = new Customer(self.getId(), self.name(), self.surname(), self.email());

        $.ajax({
            type: "POST",
            url: 'api/customers',
            data: ko.toJSON({
                data: customer
            }),
            success: function (result) {
                self.Customers.push(customer);

                self.name("");
                self.surname("");
                self.email("");

            },
            error: function (err) {
                alert(err.status + " - " + err.statusText);
            }
        });

        $('#create-customer').modal('toggle');

    };

    self.deleteItem = function ($this) {

        $.ajax({
            type: "DELETE",
            url: 'api/customers/' + this.id,

            success: function (result) {
                self.Customers.remove($this);
                $('#delete-customer').modal('toggle');
            },
            error: function (err) {
                alert(err.status + " - " + err.statusText);
            }
        });

    };

    self.callEditCustomerFromViewCustomer = function () {
        $('#display-customer').modal('toggle');
        $('#edit-customer').modal('toggle');
    };

    self.editCustomer = function ($this) {

        var customer = self.selectedCustomer();

        $.ajax({
            type: "PUT",
            url: 'api/customers/' + this.id,
            contentType: 'application/json',
            data: ko.toJSON({
                data: customer
            }),
            success: function (result) {
                self.Customers.remove($this);
                self.Customers.push($this);
                $('#edit-customer').modal('toggle');
            },
            error: function (err) {
                alert(err.status + " - " + err.statusText);
            }
        });

    };

    self.filteredCustomer = ko.computed(function () {

        var filterTextId = self.filterId().toLowerCase(),
            filterTextName = self.filterName().toLowerCase(),
            filterTextSurname = self.filterSurname().toLowerCase();

        if (!filterTextId && !filterTextName && !filterTextSurname) {
            return self.Customers();
        } else {
            if (self.Customers() != 'undefined' && self.Customers() !== null && self.Customers().length > 0) {
                return ko.utils.arrayFilter(self.Customers(), function (item) {
                    return (stringStartsWith(item.id.toLowerCase(), filterTextId) && stringStartsWith(item.name.toLowerCase(), filterTextName) && stringStartsWith(item.surname.toLowerCase(), filterTextSurname));
                });
            }
        }

    });

    // Used for the "Create New Order" view

    self.filteredCustomer2 = ko.computed(function () {

        var filterTextId2 = self.filterId2().toLowerCase();
        var filterTextName2 = self.filterName2().toLowerCase();
        var filterTextSurname2 = self.filterSurname2().toLowerCase();

        if (!filterTextId2 && !filterTextName2 && !filterTextSurname2) {
            return self.Customers();
        } else {
            if (self.Customers() != 'undefined' && self.Customers() !== null && self.Customers().length > 0) {
                return ko.utils.arrayFilter(self.Customers(), function (item) {
                    return (stringStartsWith(item.id.toLowerCase(), filterTextId2) && stringStartsWith(item.name.toLowerCase(), filterTextName2) && stringStartsWith(item.surname.toLowerCase(), filterTextSurname2));
                });
            }
        }

    });

};

// Product View Model starts here

var ProductViewModel = function () {

    var stringStartsWith = function (string, startsWith) {
        string = string || "";
        if (startsWith.length > string.length)
            return false;
        return string.substring(0, startsWith.length) === startsWith;
    };


    function Product(id, name, price) {

        this.id = id;
        this.name = name;
        this.price = price;

    }


    var self = this;

    self.name = ko.observable("");
    self.price = ko.observable("");
    self.filterId = ko.observable("");
    self.filterName = ko.observable("");
    self.filterPrice = ko.observable("");
    self.selectedProduct = ko.observable(null);
    self.Products = ko.observableArray();

    // used for "create new order - add items" view

    self.filterProductId2 = ko.observable("");
    self.filterProductName2 = ko.observable("");
    self.filterProductPrice2 = ko.observable("");
    self.selectedProduct2 = ko.observable(null);
    self.selectedProductInOrder = ko.observable("");

    self.cart = ko.observableArray("");

    self.addToCart = function () {
        alert("Item added to cart");
        self.cart.push(this);
    };

    self.removeFromCart = function ($this) {
        alert("this is a test");
//        self.cart.remove($this);
    };

    self.getId = function () {

        var idCounter;

        if (self.Products().length === 0) {
            idCounter = 0;
        } else {
            idCounter = self.Products()[self.Products().length - 1]['id'];
        }
        return (++idCounter);
    };

    self.clearSearchProducts = function () {
        self.filterId("");
        self.filterName("");
        self.filterPrice("");
    };

    self.clearSearchProducts2 = function () {
        self.filterProductId2("");
        self.filterProductName2("");
        self.filterProductPrice2("");
    };

    $.getJSON("api/products", function (data) {
        self.Products(data);
    });

    self.Products.push(new Product(1,"product 1", "300"));
    self.Products.push(new Product(2,"product 2", "400"));
    self.Products.push(new Product(3,"product 3", "500"));
    self.Products.push(new Product(4,"product 4", "600"));


    self.createNewProduct = function () {

        var product = new Product(self.getId(), self.name(), self.price());

        $.ajax({
            type: "POST",
            url: 'api/products',
            data: ko.toJSON({
                data: product
            }),
            success: function (result) {
                self.Products.push(product);

                self.name("");
                self.price("");

            },
            error: function (err) {
                alert(err.status + " - " + err.statusText);
            }
        });

        $('#create-product').modal('toggle');

    };

    self.deleteItem = function ($this) {

        $.ajax({
            type: "DELETE",
            url: 'api/products/' + this.id,

            success: function (result) {
                self.Products.remove($this);
                $('#delete-product').modal('toggle');
            },
            error: function (err) {
                alert(err.status + " - " + err.statusText);
            }
        });

    };

    self.callEditProductFromViewProduct = function () {
        $('#display-product').modal('toggle');
        $('#edit-product').modal('toggle');
    };

    self.editProduct = function ($this) {

        var product = self.selectedProduct();

        $.ajax({
            type: "PUT",
            url: 'api/products/' + this.id,
            contentType: 'application/json',
            data: ko.toJSON({
                data: product
            }),
            success: function (result) {
                self.Products.remove($this);
                self.Products.push($this);
                $('#edit-product').modal('toggle');
            },
            error: function (err) {
                alert(err.status + " - " + err.statusText);
            }
        });

    };

    self.filteredProducts = ko.computed(function () {

        var filterTextId = self.filterId().toLowerCase(),
            filterTextName = self.filterName().toLowerCase(),
            filterTextPrice = self.filterPrice().toLowerCase();

        if (!filterTextId && !filterTextName && !filterTextPrice) {
            return self.Products();
        } else {
            if (self.Products() !== 'undefined' && self.Products() !== null && self.Products().length > 0) {
                return ko.utils.arrayFilter(self.Products(), function (item) {
                    return (stringStartsWith(item.id.toLowerCase(), filterTextId) && stringStartsWith(item.name.toLowerCase(), filterTextName) && stringStartsWith(item.price.toLowerCase(), filterTextPrice));
                });
            }
        }

    });

    // used for "create new order - add item" view

    self.filteredProducts2 = ko.computed(function () {

        var filterProductTextId2 = self.filterProductId2().toLowerCase(),
            filterProductTextName2 = self.filterProductName2().toLowerCase(),
            filterProductTextPrice2 = self.filterProductPrice2().toLowerCase();

        if (!filterProductTextId2 && !filterProductTextName2 && !filterProductTextPrice2) {
            return self.Products();
        } else {
            if (self.Products() !== 'undefined' && self.Products() !== null && self.Products().length > 0) {
                return ko.utils.arrayFilter(self.Products(), function (item) {
                    return (stringStartsWith(item.id.toLowerCase(), filterProductTextId2) && stringStartsWith(item.name.toLowerCase(), filterProductTextName2) && stringStartsWith(item.price.toLowerCase(), filterProductTextPrice2));
                });
            }
        }

    });



};

// CustomerOrderViewModel starts here

var CustomerOrderViewModel = function () {

    function CustomerOrder(id, date, customer, details) {

        this.id = id;
        this.date = name;
        this.customer = customer;
        this.details = details;

    }

    var self = this;

    self.id = ko.observable("");
    self.date = ko.observable();
    self.customer = ko.observable("");
    self.details = ko.observable("");
    self.selectedOrder = ko.observable(null);
    self.CustomerOrders = ko.observableArray("");



    var newOrder = {
        id: 1,
        date: "10/10/20",
        customer: "ThecUstomeRhere",
        details: "sajdasdj"
    };

    self.createOrder = function () {
        alert("Order is created!")
    };



    self.CustomerOrders.push(newOrder);

    self.callEditOrderFromViewOrder = function () {
        $('#display-order').modal('toggle');
        $('#edit-order').modal('toggle');

    };

    self.deleteItem = function ($this) {

        $.ajax({
            type: "DELETE",
            url: 'api/orders/' + this.id,

            success: function (result) {
                self.CustomerOrders.remove($this);
                $('#delete-order').modal('toggle');
            },
            error: function (err) {
                $('#delete-order').modal('toggle');
                alert(err.status + " - " + err.statusText);
            }
        });

    };

    self.editOrderItem = function ($this) {

        var selectedCustomerOrder = self.selectedOrder();

        $.ajax({
            type: "PUT",
            url: 'api/orders/' + this.id,
            contentType: 'application/json',
            data: ko.toJSON({
                data: selectedCustomerOrder
            }),
            success: function (result) {
                self.CustomerOrders.remove($this);
                self.CustomerOrders.push($this);
                $('#edit-order').modal('toggle');
            },
            error: function (err) {
                alert(err.status + " - " + err.statusText);
            }
        });

    };

};

var masterVM = {
    customerVM: new CustomerViewModel(),
    productVM: new ProductViewModel(),
    customerOrderVM: new CustomerOrderViewModel()
};

ko.applyBindings(masterVM);

1 个答案:

答案 0 :(得分:3)

您对removeFromCart的绑定错误。它在绑定时调用函数,当cart可观察数组在foreach绑定中发生更改时会发生这种情况。

替换click: $parent.removeFromCart($data)
使用click: $parent.removeFromCart

Demo