如何使用 KnockoutJS 和 JQuery UI 创建自动完成组合框

Posted

技术标签:

【中文标题】如何使用 KnockoutJS 和 JQuery UI 创建自动完成组合框【英文标题】:How to create an autocomplete combobox with KnockoutJS and JQuery UI 【发布时间】:2020-04-03 18:11:51 【问题描述】:

我一直在尝试根据this post 中接受的响应创建一个自动完成下拉菜单,但自动完成下拉菜单根本没有出现。可能是因为响应是 9 岁,或者我做错了什么。我已经尝试了我遇到的所有建议。是否有使用 jquery 1.12.3 版、jquery-ui 1.12.1 版和 knockoutjs 3.4.1 版创建此组合框的更新方法?

对我来说,绑定似乎并没有真正发生,因为我可以将自定义绑定重命名为“jqAuto1”而不是“jqAuto”,并且不会出现错误,即使“jqAuto1”没有在任何地方定义。为什么不被捡起来?

这是我的代码。请注意,JS 脚本与 CShtml 和 TS 文件位于单独的父解决方案中。浏览器仍然会找到并执行 JS 脚本。

CSHTML

<input class="form-control form-control-xs" data-bind="value: companyName, jqAuto:  autoFocus: true , jqAutoSource: myComp, jqAutoValue: mySelectedGuid, jqAutoSourceLabel: 'displayName', jqAutoSourceInputValue: 'coname', jqAutoSourceValue: 'id'" placeholder="Type Company Name and select from list" />

TS

// For list of Companies
class Comp 
    _id: KnockoutObservable<string>;
    _coname: KnockoutObservable<string>;
    _coid: KnockoutObservable<string>;

    constructor(id: string, coname: string, coid: string) 
        this._id = ko.observable(id);
        this._coname = ko.observable(coname);
        this._coid = ko.observable(coid);
    


myComp: KnockoutObservableArray<Comp>;
mySelectedGuid: KnockoutObservable<string>;
displayName: KnockoutComputed<string>;

...

this.myComp = ko.observableArray([
            new Comp("1", "Company 1", "CO1"),
            new Comp("2", "Company 2", "CO2"),
            new Comp("3", "Company 3", "CO3"),
            new Comp("4", "Company 4", "CO4"),
            new Comp("5", "Company 5", "CO5")
        ]);

this.companyName = ko.validatedObservable<string>("");
this.displayName = ko.computed(function () 
            return this.myComp.coname + " [" + this.myComp.coid + "]";
        , this);
this.mySelectedGuid = ko.observable("5");

JS 几乎是链接帖子中的内容

(function () 
    var global = this || (0, eval)('this'),
        document = global['document'],
        moduleName = 'knockout-binding-jqauto',
        dependencies = ['knockout', 'jquery'];

    var moduleDance = function (factory) 
        // Module systems magic dance.

        if (typeof define === "function" && define["amd"]) 
            define(moduleName, dependencies.concat('exports'), factory);
         else 
            // using explicit <script> tags with no loader
            global.CPU = global.CPU || ;
            factory(global.ko, global.Globalize);
        
    ;

    var factory = function (ko, $) 

        ko.bindingHandlers.jqauto = 
            init: function (element, valueAccessor, allBindings, viewModel, bindingContext) 
                var options = valueAccessor() || ,
                    allBindings = allBindingsAccessor(),
                    unwrap = ko.utils.unwrapObservable,
                    modelValue = allBindings.jqAutoValue,
                    source = allBindings.jqAutoSource,
                    valueProp = allBindings.jqAutoSourceValue,
                    inputValueProp = allBindings.jqAutoSourceInputValue || valueProp,
                    labelProp = allBindings.jqAutoSourceLabel || valueProp;

                //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();
                    alert(currentValue);
                    var matchingItem = ko.utils.arrayFirst(unwrap(source), function (item) 
                        return unwrap(inputValueProp ? item[inputValueProp] : item) === currentValue;
                    );

                    if (!matchingItem) 
                        writeValueToModel(null);
                    
                


                //handle the choices being updated in a DO, to decouple value updates from source (options) updates
                var mappedSource = ko.dependentObservable(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;
                , null,  disposeWhenNodeIsRemoved: element );

                //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();

                //initialize autocomplete
                $(element).autocomplete(options);

            ,

            update: function (element, valueAccessor, allBindings, 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;
                    ) || ;  //probably don't need the || , but just protect against a bad value          
                

                //update the element with the value that should be shown in the input
                $(element).val(modelValue && inputValueProp !== valueProp ? unwrap(modelValue[inputValueProp]) : modelValue.toString());
            

        
    ;

    moduleDance(factory);
)();

【问题讨论】:

这个怎么样? jqueryui.com/autocomplete @panoskarajohn 淘汰赛需要通过自定义绑定连接到自动完成。我试过了:ko.bindingHandlers.jqAutoComplete = update: function (element, valueAccessor, allBindingsAccessor, viewModel) var companies = valueAccessor(); $(element).autocomplete( source: valueAccessor() ); ; @panoskarajohn 然后我尝试像这样使用它:&lt;input data-bind="value: companyName, jqAutoComplete: companies"/&gt;。还是不行 我会发布一个答案。我没有完全理解你的问题。但是为什么你甚至需要淘汰赛? @panoskarajohn 在我之前参与这个项目的团队集成了淘汰赛并以我需要使用自定义绑定或解构在我之前完成的所有内容的方式构建项目 【参考方案1】:

我还没有完全理解你的问题。但淘汰赛与 UIComplete 无关。请看一个使用 UI Complete 的简单示例。

async function autocomplete() 
    const sthings= await getSthings(); //gets json array, or ajax call, this is a promise
    $("#sthHighlightSearch").autocomplete(
        source: sthings
    );
    //This is an extension method for autocomplete
    //Should filter the list with starts with characters written in the autocomplete
    $.ui.autocomplete.filter = function (array, term) 
        var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(term), "i");
        return $.grep(array, function (value) 
            return matcher.test(value.label || value.value || value);
        );
    ;


【讨论】:

以上是关于如何使用 KnockoutJS 和 JQuery UI 创建自动完成组合框的主要内容,如果未能解决你的问题,请参考以下文章

使用 jquery-ui datepicker 的 knockoutjs 数据绑定

通过 Jquery Ajax 函数加载后不渲染 KnockoutJS 元素

我应该花精力实现 knockoutjs 还是研究 jQuery Data Link [关闭]

将 jquery ui 对话框与 knockoutjs 集成

KnockoutJS 设置 jQuery Mobile 滑块的最大选项

使用 KnockoutJS 创建可重用组件