Updating Change Knockout Multiselect value from jQ

2019-08-15 19:41发布

问题:

I'm working on making a few tweaks to a project that uses Knockout, and I can't for the life of me figure out how to update the value of a multiselect from jQuery. I'm sure there's ways to do it natively in Knockout, but it's a tiny change and I really don't want to have to dig into it.

The html for the select box is this:

<select id="projectSelect" 
    data-bind="multiSelect: { items: projects, value: filteredProject, header: false, multiple: false }, 
    optionsText: 'name', 
    optionsValue: 'id', 
    optionsCaption: 'All Projects'" 
    style="display: none;">
</select>

And defined in Knockout like this:

this.filteredProject = ko.observable(null);

this.filteredProject.subscribe(function(projectId)
        {
            for (var i = 0; i < self.projects.length; i++)
                if (self.projects[i].id == projectId)
                {
                    self.filteredProjectObject(self.projects[i]);
                    self.selectedProjectCheckboxDisabled(false);
                    return
                }
            self.onlyShowHoursForSelectedProject(false);
            self.selectedProjectCheckboxDisabled(true);
            self.filteredProjectObject(null)
        });
this.filteredProjectObject = ko.observable(null);

And my current jQuery is :

var projSelect = $("#projectSelect");

projSelect.val(projId).change();
projSelect.multiselect("refresh").change()

And that jQuery runs on a button press.

When the jQuery runs, the multiselect updates with the new value, but the code tied to it never runs, and no matter what I do I can't force Knockout to update. Tearing my hair out here, so any help will be very much appreciated.

回答1:

Knockout and jQuery don't work well in this manner. You should really try to update the ViewModel, and let Knockout update the view. I can nearly guarantee that persisting in your current approach (using jQuery to update the DOM, and getting KnockoutJS to respond to that) is bound to be harder and more troublesome in the long run.

However, if you insist, I'd think you need to use trigger to notify KnockoutJS via regular DOM events that a different select option was chosen. For your code that would probably be:

projSelect.val(projId).trigger('change');

To demonstrate how this would work:

var ViewModel = function() {
  var self = this;
  self.items = ["apple", "orange", "kiwi"];
  self.currentItem = ko.observable(null);
};

var vm = new ViewModel();

ko.applyBindings(vm);

$('#kiwi').on("click", function() {
  $("#sel").val("kiwi").trigger('change'); // Major hack! Try to avoid needing this.
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<select id="sel" data-bind="options: items, value: currentItem"></select>
<hr />
<button id="kiwi">Set KIWI with jQuery</button>
<hr />
<pre data-bind="text: ko.toJSON($root)"></pre>