错误的BindingHandler被调用

时间:2014-07-28 15:22:55

标签: javascript jquery knockout.js

我遇到一个非常奇怪的问题,即下拉列表更改时会调用错误的绑定处理程序。我正在使用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>

2 个答案:

答案 0 :(得分:1)

update回调旨在重绘DOM以响应模型更改,而不是地点to update model when DOM changes(正如您的代码所暗示的那样)。

仅在update回调中读取模型的值才能更新DOM,永远不会在update回调中改变模型。

value绑定(值:deptID)将DOM与模型(deptID)同步,您不需要另一个bindingHandler。

你想要的只是do something when model changes

  1. 摆脱ko.bindingHandlers.departmentSelectko.bindingHandlers.serviceAreaSelectko.bindingHandlers.employeeSelect
  2. 删除绑定 departmentSelect: deptIDserviceAreaSelect: ServiceAreaIDemployeeSelect: EmployeeID
  3. 将模型中的逻辑作为您自己的答案建议。
  4. 是的,您的部分代码仍然不是ko样式,即self.deptName($('#AssignedDepartment option:selected').text())

    理想情况下,在ko app中,所有DOM操作都应该隐藏在bindingHandler的initupdate回调中。

    有两种方法可以解决它:

    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%淘汰赛,但这是我能让它发挥作用的唯一方式。