当选择的下拉值更改时,Knockout observable不会更新

时间:2016-06-23 20:13:29

标签: knockout.js webpack jquery-chosen commonjs

我们一直在使用Chosen库和RequireJs以及KnockOut。在我们从RequireJS切换到commonjs并使用webpack捆绑之前,一切正常。问题是,当我们更改knockout observable下拉列表中的值时,chosen不会更新。

以下是使用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可观察时,它在下拉列表更改时不会更新值。我只在初始加载时看到控制台的值。

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

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

有关如何解决此问题或导致此问题的任何建议?

1 个答案:

答案 0 :(得分:0)

问题是由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');

    }

};