如何创建自动完成组合框? [英] How to create an auto-complete combobox?

查看:15
本文介绍了如何创建自动完成组合框?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人知道使用 Knockout JS 模板创建自动完成组合框的最佳方法吗?

Does any one know the best way to create an autocomplete combobox with Knockout JS templates?

我有以下模板:

<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 代码一起使用,但收效甚微.

Sometimes this list is long and I'd like to have Knockout play nicely with perhaps jQuery autocomplete or some straight JavaScript code, but have had little success.

此外,jQuery.Autocomplete 需要一个输入字段.有什么想法吗?

In addition, jQuery.Autocomplete requires an input field. Any ideas?

推荐答案

这是我编写的 jQuery UI 自动完成绑定.它旨在反映与选择元素一起使用的 optionsoptionsTextoptionsValuevalue 绑定范例添加项(您可以通过 AJAX 查询选项,您可以区分输入框中显示的内容与弹出的选择框中显示的内容.

Here is a jQuery UI Autocomplete binding that I wrote. It is intended to mirror the options, optionsText, optionsValue, value binding paradigm used with select elements with a couple of additions (you can query for options via AJAX and you can differentiate what is displayed in the input box vs. what is displayed in the selection box that pops up.

您不需要提供所有选项.它将为您选择默认值.

You do not need to provide all of the options. It will choose defaults for you.

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

Here is a sample without the AJAX functionality: http://jsfiddle.net/rniemeyer/YNCTY/

这是带有按钮的相同示例,使其表现得更像一个组合框:http://jsfiddle.net/rniemeyer/PPsRC/

Here is the same sample with a button that makes it behave more like a combo box: http://jsfiddle.net/rniemeyer/PPsRC/

以下是通过 AJAX 检索选项的示例:http://jsfiddle.net/rniemeyer/MJQ6g/

Here is a sample with the options retrieved via 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/淘汰赛-jqAutocomplete

UPDATE: I am maintaining a version of this binding here: https://github.com/rniemeyer/knockout-jqAutocomplete

这篇关于如何创建自动完成组合框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆