我使用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">
×
</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);
答案 0 :(得分:3)
您对removeFromCart
的绑定错误。它在绑定时调用函数,当cart
可观察数组在foreach
绑定中发生更改时会发生这种情况。
替换click: $parent.removeFromCart($data)
使用click: $parent.removeFromCart