jQuery Chosen doesn't update select options wh

2020-08-15 07:42发布

问题:

I am trying to make jQuery Chosen and KnockoutJS work at the same time.

The problem is "jQuery Chosen" refuses to update options list even though I've created custom binding for it.

Here is the example - http://jsfiddle.net/5fGAf/

I have two changeable selects - "Country" and "Method". "Method" options list depends on country selected. When I select the country for the first time - everything works perfect. But when I want to change the country - "Method" options list remains the same, even though corresponding knockout computed value is updated.

If I manually run $(".chosen-select").trigger('chosen:updated') in the browser console - options list updates.

Custom binding code:

ko.bindingHandlers.chosen = {
  init: function(element) {             
    $(element).chosen({disable_search_threshold: 10});
  },
  update: function(element) {
    $(".chosen-select").trigger('chosen:updated');
  }
};

回答1:

You have two problems:

  • in your fiddle there is no .chosen-select so your update function does not find the select but anyway you should use $(element) to access the currently bound element
  • in KO 3.0 bindings are fired independently. Because your chosen binding is not connected to the your observable array your update won't fire when you change that array.

You can solve this "update" problem with explicitly declaring a dependency on the options binding in your custom binding but a better solution would be to delegate to it:

ko.bindingHandlers.chosen = {
    init: function(element)  {
        ko.bindingHandlers.options.init(element);
        $(element).chosen({disable_search_threshold: 10});
    },
    update: function(element, valueAccessor, allBindings) {
        ko.bindingHandlers.options.update(element, valueAccessor, allBindings);
        $(element).trigger('chosen:updated');
    }
};

And use it where you would normally use the options binding:

<select id="option1" class="form-control" 
    data-bind="chosen: payoutOptions, 
               optionsText: 'optionText', 
               optionsValue: 'optionValue', 
               value: activePayoutOption"></select>

Demo JSFiddle.



回答2:

My solution is this:

ko.bindingHandlers.chosen =
{
    init: function (element, valueAccessor, allBindings) {
        $(element).chosen(valueAccessor());

        // trigger chosen:updated event when the bound value or options changes

        $.each('value|selectedOptions|options'.split("|"), function (i, e) {
            var bv = allBindings.get(e);
            if (ko.isObservable(bv))
                bv.subscribe(function () { $(element).trigger('chosen:updated'); });
        });
    },
    update: function (element) {
        $(element).trigger('chosen:updated');
    }
};

You'd use it like so:

<select data-bind="
    options: payoutOptions, 
    optionsText: 'optionText', 
    optionsValue: 'optionValue',
    value: activePayoutOption,
    chosen: { disable_search_threshold: 10, width:'100%' }">
</select>

Note that

  1. The chosen binding is option is added...rather than changing the way the given binding works
  2. Chosen options ({ width:'100%',... }) are not hardwired in the handler


回答3:

I used a method that is also compatible with all my existing bindings, so I didn't need to go though a ton of html files removing the options binding.

ko.bindingHandlers.chosen = {
    init: function(element, valueAccessor, allBindings) {
        $(element).chosen({ disable_search_threshold: 10});
        var valueObservable = allBindings.get('value');
        var optionsObservable = allBindings.get('options');

        var updateList = function() {
            $(element).trigger('chosen:updated');
        }

        if (valueObservable && typeof(valueObservable.subscribe) == 'function') {
            valueObservable.subscribe(updateList);
        }

        if (optionsObservable && typeof(optionsObservable.subscribe) == 'function') {
            optionsObservable.subscribe(updateList);
        }

        $(element).chosen({ disable_search_threshold: 7, width:'100%' });
    }
};