Knockout observable is not updating when Chosen dropdown value changes

Knockout observable is not updating when Chosen dropdown value changes

我们一直在将 Chosen 库与 RequireJsKnockOut 一起使用。一切正常,直到我们从 RequireJS 切换到 commonjs 并且现在使用 webpack 进行捆绑。问题是当我们更改 chosen 下拉列表中的值时 knockout observable 不会更新。

这是使用 RequireJs 的 javascript 代码。

define(['knockout', 'text!./employee-setup.html', 'utils', 'panel-section', 'toastr', 'jquery', 'knockout-postbox', 'knockout-projections', 'chosen', 'jsteps'], function (ko, template, utils, PanelSection, toastr, $, _, _, _, jsteps) {
function EmployeeSetup(params) {
    var self = this;
    this.agentTypes = ko.observableArray();
    this.agentType = ko.observable();


    this.loadAgentTypes = function () {
        $.ajax({
            url: '/Employee/GetAgentTypes',
            method: 'POST',
            dataType: 'json',
            success: function (result) {
                if (utils.handleAjaxResult(result) && result.Data) {
                    self.agentTypes([]);

                    var agentType = [{ ID: "", Name: "" }];

                    $.each(result.Data, function (i, item) {
                        agentType.push({ID: item.ID, Name: item.Name});
                    });
                    self.agentTypes(agentType);
                    $('#agentType').chosen({ allow_single_deselect: true, width: '310px' });
                    $('#agentType').trigger("chosen:updated");
                } else {
                }

            },
            error: function () {
                toastr.error('Could not load agent types');
            }
        });
    };
    self.loadAgentTypes();
    };
 return { template: template, viewModel: EmployeeSetup };
});

该组件的 html:

<div class="input-container" data-bind="">
     <select data-bind="value: agentType, options: agentTypes, optionsText: 'Name'" data-placeholder="Select Agent Type..." id="agentType" class="chosen-select sp-uin-dropdown" tabindex="2"> </select>
</div>

这是使用 commonjs

的代码
var ko = require('knockout'),
    utils = require('utils'),
    PanelSection = require('panel-section'),
    toastr = require('toastr'),
    $ = require('jquery');
require('knockout-postbox');

function ViewModel(params) {
   var self = this;
   this.agentTypes = ko.observableArray();
   this.agentType = ko.observable();

   this.loadAgentTypes = function () {
   $.ajax({
       url: '/Employee/GetAgentTypes',
       method: 'POST',
       dataType: 'json',
       success: function (result) {
       if (utils.handleAjaxResult(result) && result.Data) {
              self.agentTypes([]);

              var agentType = [{ ID: "", Name: "" }];

              $.each(result.Data, function (i, item) {
                  agentType.push({ID: item.ID, Name: item.Name});
              });
              self.agentTypes(agentType);
              $('#agentType').chosen({ allow_single_deselect: true, width: '310px' });
              $('#agentType').trigger("chosen:updated");
            } else {
           }
        },
        error: function () {
           toastr.error('Could not load agent types');
        }
    });
  };
  self.loadAgentTypes();
}
module.exports = { viewModel: ViewModel, template: require('./template.html')      };

它使用与上面相同的 html 文件。

webpack.config.js中我们定义了jquerychosen的路径。

它正确加载了 chosen dropdown。但是,当我 subscribe 到 observable 时,它​​不会在下拉列表更改时更新值。我只在初始加载时从控制台看到一次值。

self.agentType.subscribe(function (value) {
    console.log('value', value);
}, this)

SO 中很少有帖子建议使用 bindingHandlers。我已经在我的应用程序中尝试了来自 JSFiddle 的这个工作代码,但我只从初始加载中获得了值。

关于如何解决这个问题或导致这个问题的原因有什么建议吗?

问题是由 webpack 引起的。为了解决这个问题,我的同事写了一个自定义bindingHandler

HTML代码:

<div class="input-container">
 <select data-bind="
                value: agentType,
                options: agentTypes, 
                optionsText: 'Name',
                dropdown: {
                    width: '310px',
                    allow_single_deselect: true
                } "
                data-placeholder="Select Agent Type..." id="agentType">
</select>

自定义 bindingHandler:

// a dropdown handler, which currently utilizes the Chosen library

ko.bindingHandlers.dropdown = {

    init: function(element, valueAccessor, allBindings, viewModel, bindingContext){

        // get chosen element

        var $element = $(element);

        // get options (if any) to pass to chosen when creating

        var options = ko.unwrap(valueAccessor());



        // NOTE: when using Chosen w/ webpack, the knockout bindings no longer

        // fired. This event handler is to remedy that. It watches the change

        // event for the underlying <select> (which chosen updates), and

        // updates the corresponding observables mapped to value and selectedOptions.

        // Only one should be bound, value for single select, selectedOptions for multi-select

        // binding direction: Knockout <- Chosen

        $element.on('change', function(e, item) {

            var valueProp = allBindings.has('value') && allBindings.get('value');

            var selectedOptionsProp = allBindings.has('selectedOptions') && allBindings.get('selectedOptions');

            if (item) {

                if (allBindings.has('options')) {

                    var allOptions = ko.unwrap(allBindings.get('options'));

                    if (valueProp) {

                        // single select

                        if (ko.isObservable(valueProp)) {

                            if (!item.isMultiple) {

                                if (item.selecting) {

                                    valueProp(allOptions[item.index]);

                                } else {

                                    valueProp(null);

                                }

                            }

                        }

                    }

                    if (selectedOptionsProp) {

                        // multi select

                        if (ko.isObservable(selectedOptionsProp)) {

                            if (item.isMultiple) {

                                // handle multi select

                                if (item.selecting) {

                                    // select

                                    selectedOptionsProp.push(allOptions[item.index]);

                                } else {

                                    // deselect

                                    selectedOptionsProp.remove(allOptions[item.index]);

                                }

                            }

                        }

                    }

                }

            } else {

                // this is triggered w/o args when the control is reset. This happens when deselecting during single-select

                if (valueProp) {

                    // single select

                    if (item === undefined && ko.isObservable(valueProp)) {

                        valueProp(null);

                    }

                }

            }

        });
        // handle updating the chosen component's UI when the underlying

        // options, selectedOptions or value changes

        // binding direction: Knockout -> Chosen

        ['options', 'selectedOptions', 'value'].forEach(function(propName){

            if (allBindings.has(propName)){

               var prop = allBindings.get(propName);

                if (ko.isObservable(prop)){

                    //console.log('subscribing to:', propName, ' for:', $element);

                    prop.subscribe(function(value){

                        if (value != null) {

                            //console.log('calling chosen:updated');

                            var options = ko.unwrap(allBindings.get('options'));

                            // console.log('got options:', options);

                            if (options) {

                                if (options.indexOf(value) > -1) {

                                    // item is in options

                                    //        console.log('value is in options:', value);

                                } else {

                                    // item is not in options, try to match ID

                                    options.some(function (item) {

                                        if (item.ID == value) {

                                            // update the obs. to the entire item, not the ID

                                            prop(item);

                                        }

                                    });

                                }

                            }

                        }

                        $element.trigger('chosen:updated');

                    });

                }

            }

        });       

        // add chosen css class (not sure this is needed)

        $element.addClass('chosen-select');



        // create chosen element, passing in options if any were specified

       if (typeof options === 'object') {

            $element.chosen(options);

        } else {

            $element.chosen();

        }

        $element.trigger('chosen:updated');

    }

};