有谁知道用Knockout JS模板创建自动完成组合框的最佳方法吗?
我有以下模板:
<script type="text/html" id="row-template">
<tr>
...
<td>
<select class="list" data-bind="options: SomeViewModelArray,
value: SelectedItem">
</select>
</td>
...
<tr>
</script>
有时候,这个清单很长,我想让Knockout在jQuery自动完成功能或一些直接的JavaScript代码方面表现出色,但收效甚微。
另外,jQuery.Autocomplete需要输入字段。有任何想法吗?
最佳答案
这是我编写的jQuery UI自动完成绑定。它旨在反映与选择元素一起使用的options
,optionsText
,optionsValue
,value
绑定范例,并带有几个附加项(您可以通过AJAX查询选项,并且可以区分输入中显示的内容框与弹出的选择框中显示的内容。
您不需要提供所有选项。它将为您选择默认值。
这是一个没有AJAX功能的示例:http://jsfiddle.net/rniemeyer/YNCTY/
这是带有按钮的相同示例,其行为使其更像组合框:http://jsfiddle.net/rniemeyer/PPsRC/
这是一个示例,其中包含通过AJAX检索的选项:http://jsfiddle.net/rniemeyer/MJQ6g/
//jqAuto -- main binding (should contain additional options to pass to autocomplete)
//jqAutoSource -- the array to populate with choices (needs to be an observableArray)
//jqAutoQuery -- function to return choices (if you need to return via AJAX)
//jqAutoValue -- where to write the selected value
//jqAutoSourceLabel -- the property that should be displayed in the possible choices
//jqAutoSourceInputValue -- the property that should be displayed in the input box
//jqAutoSourceValue -- the property to use for the value
ko.bindingHandlers.jqAuto = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
var options = valueAccessor() || {},
allBindings = allBindingsAccessor(),
unwrap = ko.utils.unwrapObservable,
modelValue = allBindings.jqAutoValue,
source = allBindings.jqAutoSource,
query = allBindings.jqAutoQuery,
valueProp = allBindings.jqAutoSourceValue,
inputValueProp = allBindings.jqAutoSourceInputValue || valueProp,
labelProp = allBindings.jqAutoSourceLabel || inputValueProp;
//function that is shared by both select and change event handlers
function writeValueToModel(valueToWrite) {
if (ko.isWriteableObservable(modelValue)) {
modelValue(valueToWrite );
} else { //write to non-observable
if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['jqAutoValue'])
allBindings['_ko_property_writers']['jqAutoValue'](valueToWrite );
}
}
//on a selection write the proper value to the model
options.select = function(event, ui) {
writeValueToModel(ui.item ? ui.item.actualValue : null);
};
//on a change, make sure that it is a valid value or clear out the model value
options.change = function(event, ui) {
var currentValue = $(element).val();
var matchingItem = ko.utils.arrayFirst(unwrap(source), function(item) {
return unwrap(item[inputValueProp]) === currentValue;
});
if (!matchingItem) {
writeValueToModel(null);
}
}
//hold the autocomplete current response
var currentResponse = null;
//handle the choices being updated in a DO, to decouple value updates from source (options) updates
var mappedSource = ko.dependentObservable({
read: function() {
mapped = ko.utils.arrayMap(unwrap(source), function(item) {
var result = {};
result.label = labelProp ? unwrap(item[labelProp]) : unwrap(item).toString(); //show in pop-up choices
result.value = inputValueProp ? unwrap(item[inputValueProp]) : unwrap(item).toString(); //show in input box
result.actualValue = valueProp ? unwrap(item[valueProp]) : item; //store in model
return result;
});
return mapped;
},
write: function(newValue) {
source(newValue); //update the source observableArray, so our mapped value (above) is correct
if (currentResponse) {
currentResponse(mappedSource());
}
}
});
if (query) {
options.source = function(request, response) {
currentResponse = response;
query.call(this, request.term, mappedSource);
}
} else {
//whenever the items that make up the source are updated, make sure that autocomplete knows it
mappedSource.subscribe(function(newValue) {
$(element).autocomplete("option", "source", newValue);
});
options.source = mappedSource();
}
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).autocomplete("destroy");
});
//initialize autocomplete
$(element).autocomplete(options);
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
//update value based on a model change
var allBindings = allBindingsAccessor(),
unwrap = ko.utils.unwrapObservable,
modelValue = unwrap(allBindings.jqAutoValue) || '',
valueProp = allBindings.jqAutoSourceValue,
inputValueProp = allBindings.jqAutoSourceInputValue || valueProp;
//if we are writing a different property to the input than we are writing to the model, then locate the object
if (valueProp && inputValueProp !== valueProp) {
var source = unwrap(allBindings.jqAutoSource) || [];
var modelValue = ko.utils.arrayFirst(source, function(item) {
return unwrap(item[valueProp]) === modelValue;
}) || {};
}
//update the element with the value that should be shown in the input
$(element).val(modelValue && inputValueProp !== valueProp ? unwrap(modelValue[inputValueProp]) : modelValue.toString());
}
};
您将使用它像:
<input data-bind="jqAuto: { autoFocus: true }, jqAutoSource: myPeople, jqAutoValue: mySelectedGuid, jqAutoSourceLabel: 'displayName', jqAutoSourceInputValue: 'name', jqAutoSourceValue: 'guid'" />
更新:我在这里维护此绑定的版本:https://github.com/rniemeyer/knockout-jqAutocomplete