我们一直在使用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
中,我们定义了jquery
和chosen
的路径。
正确加载chosen dropdown
。但是,当我subscribe
可观察时,它在下拉列表更改时不会更新值。我只在初始加载时看到控制台的值。
self.agentType.subscribe(function (value) {
console.log('value', value);
}, this)
SO中的帖子很少建议使用bindingHandlers
。我在我的应用程序中尝试了来自JSFiddle的这个工作代码,但我只从初始加载中获得了值。
有关如何解决此问题或导致此问题的任何建议?
答案 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');
}
};