如何创build一个自动完成的combobox?

有没有人知道用Knockout JS模板创build自动完成combobox的最佳方法?

我有以下模板:

<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需要一个input字段。 有任何想法吗?

这是我写的一个jQuery UI自动完成绑定。 它旨在镜像optionsoptionsTextoptionsValue ,与select元素一起使用的value绑定范例以及一些添加(您可以通过AJAX查询选项,并且可以区分在input框中显示的内容与在popupselect框。

你不需要提供所有的选项。 它会为你select默认值。

这是一个没有AJAXfunction的示例: http : //jsfiddle.net/rniemeyer/YNCTY/

这里是一个button,使其行为更像一个combobox相同的示例: 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

这是我的解决scheme:

 ko.bindingHandlers.ko_autocomplete = { init: function (element, params) { $(element).autocomplete(params()); }, update: function (element, params) { $(element).autocomplete("option", "source", params().source); } }; 

用法:

 <input type="text" id="name-search" data-bind="value: langName, ko_autocomplete: { source: getLangs(), select: addLang }"/> 

http://jsfiddle.net/7bRVH/214/与RP相比,这是非常基本的,但也许满足您的需求。;

需要处理…

这两种解决scheme都非常棒(尼迈耶的粒度更细),但都忘记了处理方式!

他们应该通过销毁jquery自动完成(防止内存泄漏)来处理处置:

 init: function (element, valueAccessor, allBindingsAccessor) { .... //handle disposal (if KO removes by the template binding) ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $(element).autocomplete("destroy"); }); } 

小的改进,

首先这些是一些非常有用的提示,谢谢大家分享。

我使用Epstone发布的版本进行了以下改进:

  1. 显示标签(而不是价值)时,按下向上或向下 – 显然这可以通过处理焦点事件

  2. 使用可观察数组作为数据源(而不是数组)

  3. 增加了乔治build议的一次性处理程序

http://jsfiddle.net/PpSfR/

 ... conf.focus = function (event, ui) { $(element).val(ui.item.label); return false; } ... 

顺便说一句,将minLength指定为0允许通过只需移动箭头键而不必input任何文本来显示替代scheme。

我用JQuery UI 1.10.x试过Niemeyer的解决scheme ,但是自动完成框没有显示,经过一些search后,我在这里find了一个简单的解决方法。 将以下规则添加到jquery-ui.css文件的末尾可以修复问题:

 ul.ui-autocomplete.ui-menu { z-index: 1000; } 

我也使用Knockout-3.1.0,所以我不得不用ko.dependentObservable(…)replace为ko.computed(…)

另外,如果您的KO View模型包含一些数值,请确保您更改比较运算符:从===到==和!==到!=,以便执行types转换。

我希望这可以帮助别人

修复RP解决scheme的负载input清除问题。 即使这是一种间接的解决scheme,我在函数的末尾改变了这个:

 $(element).val(modelValue && inputValueProp !== valueProp ? unwrap(modelValue[inputValueProp]) : modelValue.toString()); 

对此:

 var savedValue = $(element).val(); $(element).val(modelValue && inputValueProp !== valueProp ? unwrap(modelValue[inputValueProp]) : modelValue.toString()); if ($(element).val() == '') { $(element).val(savedValue); } 

尼迈耶的解决scheme是伟大的,但是我遇到了一个问题,当试图在模态内使用自动完成。 自动完成在模式closures事件中被销毁(未捕获的错误:在初始化之前不能调用自动完成的方法;试图调用方法的选项)我通过向绑定的订阅方法添加两行来修复它:

 mappedSource.subscribe(function (newValue) { if (!$(element).hasClass('ui-autocomplete-input')) $(element).autocomplete(options); $(element).autocomplete("option", "source", newValue); }); 

我知道这个问题是旧的,但我也正在寻找一个非常简单的解决scheme,我们的团队使用这种forms,发现jQuery自动完成提出了一个“autocompleteselect”事件 。

这给了我这个想法。

 <input data-bind="value: text, valueUpdate:['blur','autocompleteselect'], jqAutocomplete: autocompleteUrl" /> 

与处理程序简单地是:

 ko.bindingHandlers.jqAutocomplete = { update: function(element, valueAccessor) { var value = valueAccessor(); $(element).autocomplete({ source: value, }); } } 

我喜欢这种方法,因为它使处理程序简单,并且不会将jQuery事件附加到我的viewmodel中。 这里是一个数组而不是一个url作为源提琴。 如果你点击文本框,并且如果你按下回车键,这将起作用。

https://jsfiddle.net/fbt1772L/3/

Epstone原始解决scheme的另一个变种。

我试图使用它,但也发现视图模型只有在手动input值时才会更新。 select一个自动完成条目将视图模型留下旧值,这有点令人担忧,因为validation仍然通过 – 只有当你看到数据库时,你才会看到问题!

我使用的方法是在淘汰赛绑定init中钩住jquery UI组件的select处理程序,当select一个值时,它简单地更新淘汰赛模型。 这个代码还包含了乔治上面有用答案的configurationpipe道。

 init: function (element, valueAccessor, allBindingsAccessor) { valueAccessor.select = function(event, ui) { var va = allBindingsAccessor(); va.value(ui.item.value); } $(element).autocomplete(valueAccessor); //handle disposal (if KO removes by the template binding) ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $(element).autocomplete("destroy"); }); } ... 
  <input class="form-control" type="text" data-bind="value: ModelProperty, ko_autocomplete: { source: $root.getAutocompleteValues() }" />