我遇到一个非常奇怪的问题,即下拉列表更改时会调用错误的绑定处理程序。我正在使用Knockout 3.1和jQuery 1.9.1。我们有一组级联下拉菜单,以便更容易找到员工。
以下是发生的事情。在页面加载时,每个处理程序都按预期调用。当我更改#AssignedDepartment
中的值时,调用serviceAreaSelect
处理程序,而不是departmentSelect
按预期调用。根本不需要调用departmentSelect
来正确填充#AssignedServiceAreaName
下拉列表。但是,当我对#AssignedServiceAreaName
进行更改时,会调用serviceAreaSelect
并按预期工作。
这里可能会发生什么?这段代码在Knockout 2.x中运行良好。
这是三个级联下拉列表系列的三个处理程序中的两个。
// Department Selected
ko.bindingHandlers.departmentSelect = {
update: function (element, valueAccessor, allBindingsAccessor, vm) {
var el = $(element).find("option:selected");
console.log("Department Selected: ", el.val());
if (el.length > 0) {
vm.deptName(el.text());
$.get('/customerservice/jsonproxy/servicearealist', { deptID: el.val() }, function (response) { vm.ServiceArea(response); }, 'json');
}
}
}
// Service Area Selected
ko.bindingHandlers.serviceAreaSelect = {
update: function (element, valueAccessor, allBindingsAccessor, vm) {
var el = $(element).find("option:selected");
console.log("Service Area Selected: ", el.val());
if (el.length > 0) {
vm.ServiceAreaName(el.text());
$.get('/customerservice/jsonproxy/employeelist', { deptID: vm.deptID, serviceAreaID: el.val() }, function (response) {
vm.EmployeeList(response[0].Employees);
vm.EmployeeList.unshift({ EmployeeName: "None Assigned", EmployeeID: "" });
}, 'json');
}
}
};
这是HTML。
<div class="row-fluid">
<div class="span4">
@Html.MyLabelFor(m => m.Department, new { @class = "control-label" })
<select class="span12" id="AssignedDepartment" name="AssignedDepartment"
data-bind="options: dept, optionsCaption: 'Select a department', optionsValue: function (item) { return item.DepartmentID }, optionsText: function (item) { return item.DepartmentName; }, value: deptID, departmentSelect: deptID"></select>
<span data-bind="text: deptID"></span><span data-bind=" text: deptName"></span>
@Html.HiddenFor(m => m.Department, new { data_bind = "value: deptName" })
@Html.HiddenFor(m => m.DepartmentCode, new { data_bind = "value: deptID" })
</div>
<div class="span4">
@Html.MyLabelFor(m => m.ServiceArea, new { @class = "control-label" })
<select class="span12" id="AssignedServiceAreaName" name="AssignedServiceArea" data-bind="options: ServiceArea, optionsCaption: 'Select a Service Area', optionsValue: function (item) { return item.ServiceAreaID; }, optionsText: function (item) { return item.ServiceAreaName; }, value: ServiceAreaID, serviceAreaSelect: ServiceAreaID"></select>
@Html.HiddenFor(m => m.ServiceArea, new { @data_bind = "value: ServiceAreaName" })
@Html.HiddenFor(m => m.ServiceAreaCode, new { @data_bind = "value: ServiceAreaID" })
@*<span data-bind="text: ServiceAreaID"></span><span data-bind=" text: ServiceAreaName"></span>*@
</div>
<div class="span4">
@Html.MyLabelFor(m => m.Employee, new { @class = "control-label" })
<select class="span12" id="AssignedEmployee" name="AssignedEmployee" data-bind="options: EmployeeList, optionsCaption: 'None Assigned', optionsValue: function (item) { return item.EmployeeID; }, optionsText: function (item) { return item.EmployeeName; }, value: EmployeeID, employeeSelect: EmployeeID"></select>
@*<span data-bind="text: EmployeeID"></span><span data-bind=" text: EmployeeName"></span>*@
@Html.HiddenFor(m => m.Employee, new { data_bind = "value: EmployeeName" })
@Html.HiddenFor(m => m.EmployeeID, new { data_bind = "value: EmployeeID" })
</div>
</div>
答案 0 :(得分:1)
update
回调旨在重绘DOM以响应模型更改,而不是地点to update model when DOM changes
(正如您的代码所暗示的那样)。
仅在update
回调中读取模型的值才能更新DOM,永远不会在update
回调中改变模型。
value
绑定(值:deptID)将DOM与模型(deptID)同步,您不需要另一个bindingHandler。
你想要的只是do something when model changes
。
ko.bindingHandlers.departmentSelect
和
ko.bindingHandlers.serviceAreaSelect
和
ko.bindingHandlers.employeeSelect
departmentSelect: deptID
,serviceAreaSelect: ServiceAreaID
和
employeeSelect: EmployeeID
。是的,您的部分代码仍然不是ko
样式,即self.deptName($('#AssignedDepartment option:selected').text())
。
理想情况下,在ko app中,所有DOM操作都应该隐藏在bindingHandler的init
和update
回调中。
有两种方法可以解决它:
A)自动更新deptName。
self.deptID.subscribe(function (val) {
if (val !== undefined && val !== null) {
$.get('/customerservice/jsonproxy/servicearealist', { deptID: val }, function (response) {
self.ServiceArea(response);
if (response.length === 1) {
self.ServiceAreaID(response[0].ServiceAreaID);
}
}, 'json');
}
});
// deptName is another representation of deptID
self.deptName = ko.computed(function() {
// I don't know whether your dept is observableArray or plain array
var depts = ko.unwrap(self.dept);
var currentOne = ko.utils.arrayFirst(depts, function(d) {
return d.DepartmentID == self.deptID();
});
return currentOne && currentOne.DepartmentName;
});
B)更改模型,不捕获deptID,捕获整个deptSelected对象。
//self.deptID = ko.observable();
self.deptSelected = ko.observable();
self.deptSelected.subscribe(function (newDept) { /* ajax */ });
// optionsText: 'DepartmentName' is enough, no need a mapping function.
<select class="span12" id="AssignedDepartment" name="AssignedDepartment"
data-bind="options: dept, optionsCaption: 'Select a department',
optionsText: 'DepartmentName', value: deptSelected"></select>
<!-- ko with: deptSelected -->
<span data-bind="text: DepartmentID"></span>
<span data-bind="text: DepartmentName"></span>
@Html.HiddenFor(m => m.Department, new { data_bind = "value: DepartmentName" })
@Html.HiddenFor(m => m.DepartmentCode, new { data_bind = "value: DepartmentID" })
<!-- /ko -->
答案 1 :(得分:0)
我有一个解决方法。我正在订阅:
self.deptID.subscribe(function (val) {
if (val !== undefined && val !== null) {
$.get('/customerservice/jsonproxy/servicearealist', { deptID: val }, function (response) {
self.ServiceArea(response);
if (response.length === 1) {
self.ServiceAreaID(response[0].ServiceAreaID);
}
}, 'json');
self.deptName($('#AssignedDepartment option:selected').text());
}
});
self.ServiceAreaID.subscribe(function (val) {
if (val !== undefined && val !== null) {
$.get('/customerservice/jsonproxy/employeelist', { deptID: self.deptID, serviceAreaID: val }, function (response) {
self.EmployeeList(response[0].Employees);
self.EmployeeList.remove({ EmployeeName: "None Assigned", EmployeeID: "" });
self.ServiceAreaName($('#AssignedServiceAreaName option:selected').text());
}, 'json');
}
});
它可能不再是100%淘汰赛,但这是我能让它发挥作用的唯一方式。