[英]Knockout.js + Bootstrap - strange thing happening
I'm using Twitter Bootstrap together with knockout.js. 我正在使用Twitter Bootstrap和kickout.js。
I have an orders page where the cashier can choose a customer and products that the customer wants to buy. 我有一个订单页面, 收银员可以选择客户 ,而且客户要购买的产品 。 However, I get some very strange behavior.
但是,我得到一些非常奇怪的行为。 When I add one product to the cart the correct function
addToCart
is called, but also the function removeFromCart
is called without me telling the program to call it. 当我将一种产品添加到购物车中时,将调用正确的函数
addToCart
,但还会调用函数removeFromCart
,而无需告诉程序调用它。 I guess something is happening because I use Bootstrap modals . 我猜发生了什么事,因为我使用了Bootstrap模态 。
Please help. 请帮忙。 Here is the fiddle http://jsfiddle.net/rY59d/4/ .
这是小提琴http://jsfiddle.net/rY59d/4/ 。
HTML Code: 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 Code: 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);
Your binding for the removeFromCart
is wrong. 您对
removeFromCart
绑定是错误的。 It calls the function when you bind, which happens when the cart
observable array changes as it is in a foreach
binding. 当您绑定时,它会调用该函数,当
cart
可观察数组随其在foreach
绑定中的变化而发生时,就会发生该函数。
Replace click: $parent.removeFromCart($data)
替换
click: $parent.removeFromCart($data)
With click: $parent.removeFromCart
click: $parent.removeFromCart
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.