使用 knockout.js 和 select2 级联下拉菜单

Posted

技术标签:

【中文标题】使用 knockout.js 和 select2 级联下拉菜单【英文标题】:Cascading dropdowns with knockout.js and select2 【发布时间】:2014-03-20 11:47:56 【问题描述】:

请看这个jsfiddle

我遇到了两个问题:

    我无法返回所选对象的 (cascadingOption) 文本。 我不知道如何忠实地将下拉列表转换为 select2。

具体来说,这个:

    <select id="make" data-bind="options: carMakers, 
                                 value: selectedMake, 
                                 optionsText : 'text', 
                                 optionsCaption : 'Select your make'">
    </select><br/>
    Selected Make: <span data-bind="text: selectedMake"></span><br/>

将 [object Object] 返回到屏幕。如果我将最后一行更改为span data-bind="text: selectedMake.text",它不会返回任何内容。但是,如果我从淘汰赛中使用subscribe 并登录到控制台,我可以返回 object.text 好吗?

第二个问题是当我通过将select2: 添加到其数据绑定属性来更改第一个选择标记时。这会正确地将下拉菜单更改为 select2 样式,但所有级联属性都会崩溃。

任何帮助或指导将不胜感激。

【问题讨论】:

【参考方案1】:

selectedMake 是一个 observable,它的值将是 cascadingOption 实例。要在您的 span:text 绑定中使用它,您需要:

<span data-bind="text: selectedMake().text"></span>

当你使用 subscribe 时它会起作用,因为你得到了 observable 的值。


Select2 尝试将选择元素与其内部状态同步。不幸的是,它依赖于每个具有 Id 值的选项。因为您不使用 optionsValue 绑定,所以这不起作用。我也使用了 select2,但我已经更改了他们的代码以使用 optionIndex 而不是 Id 和一个相当复杂的 select2 绑定来管理差异,例如 Ajax 和多选。

但是,您的示例使用 select2...

    创建一个 observable 来存储 Id。 更改您的值绑定以使用 Id observable 添加一个 optionsValue 绑定以从您的源选项返回一个唯一的 Id。这将用于同步 select2 选项和 select 选项。 添加对 Id observable 的订阅以过滤选项并从选项数组中返回对象 为可观察值添加订阅以保持 Id 同步(注意 2 个订阅之间的无限循环)

我已更新您的JSFiddle,但差异如下。在此我创建了一个包装函数来构建一个可观察/ id 对,并在两者之间进行订阅。 select绑定有不同的value绑定值,增加了optionValue绑定 span 绑定具有不同的文本绑定值。

HTML

<div>
    <select id="make" data-bind="options: carMakers, value: selectedMake.id, optionsValue: 'text', optionsText : 'text', optionsCaption : 'Select your make', select2: "></select><br/>
    Selected Make: <span data-bind="text: selectedMake().text"></span><br/>
    <select id="type" data-bind="options: carTypes, value: selectedType.id, optionsValue: 'text', optionsText : 'text', optionsCaption : 'Select your type', enable : carTypes, select2: "></select><br/>
    Selected Model: <span data-bind="text: selectedType().text"></span><br/>
    <select id="model" data-bind="options: carModels, value: selectedModel.id, optionsValue: 'text', optionsText : 'text', optionsCaption : 'Select your Model', enable: carModels, select2: "></select><br/>
    Selected Model: <span data-bind="text: selectedModel().text"></span><br/>
</div>

Javascript

var makeObservableForSelect2 = function( sourceOptions, idSelector ) 
    var target = ko.observable();
    target.id = ko.observable();

    target.id.subscribe( function(id) 
        var realSource = ko.unwrap(sourceOptions)
        if ( !realSource ) 
            return;
        ;
        // Don't set target if id already matches to stop infinite loop.
        if ( target() && target()[idSelector] === id ) 
            return;
        

        target( realSource.filter( function(item)  return item[idSelector] === id;  )[0] ||  );
     );

    target.subscribe( function(value) 
        // Don't set id if id already matches to stop infinite loop.
        if ( target.id() && value[idSelector] === target.id() ) 
            return;
        

        target.id(value[idSelector]);
    );

    return target;
;

var viewModel = 
    carMakers: buildData()
;

viewModel.selectedMake = makeObservableForSelect2( viewModel.carMakers, 'text');

viewModel.carTypes = ko.computed(function()
    return viewModel.selectedMake() ? viewModel.selectedMake().childOptions : null;
);

viewModel.selectedType = makeObservableForSelect2( viewModel.carTypes, 'text');

viewModel.carModels = ko.computed(function()
    return viewModel.selectedType() ? viewModel.selectedType().childOptions : null;
);

viewModel.selectedModel = makeObservableForSelect2( viewModel.carModels, 'text');

【讨论】:

【参考方案2】:

工作小提琴:http://jsfiddle.net/jiggle/Lw2qJ/

问题1:

你可以做span data-bind="text: selectedMake().text"(注意括号),但前提是 selectedMake 总是有一个值(因此有 .text 属性)。

还有其他一些方法可以做到这一点,在http://www.knockmeout.net/2011/08/simplifying-and-cleaning-up-views-in.html 上进行了概述

问题 2:

但是,当我开始查看问题 2 时,我发现必须设置 optionsValue 属性才能使 select2 正常工作(尽管有人可以对此进行纠正),所以我进行了一些重构,因此您的 selectedMake 不再是对象,而是 text 属性和 optionsValue:"text" 像这样:

<select id="make" data-bind="select2: , options: carMakers, value:    selectedMake,   
optionsValue:'text', 
optionsText : 'text', optionsCaption : 'Select your make'"></select><br/>

这意味着对于下一个级别的级联计算,您必须将其更改为首先从 make 的选定文本值中查找 make,如下所示:

viewModel.carTypes = ko.computed(function()
if(viewModel.selectedMake())
    var make = ko.utils.arrayFirst(viewModel.carMakers,function(item)
        console.log(item.text,viewModel.selectedMake());
            return item.text===viewModel.selectedMake();          
    );
    return make.childOptions;
 
);

这也意味着您可以只使用Selected Make: &lt;span data-bind="text: selectedMake"&gt;&lt;/span&gt; 而不是使用方括号,因为您不会尝试访问可观察对象的属性。

这又是小提琴:http://jsfiddle.net/jiggle/Lw2qJ/

希望对你有帮助。

【讨论】:

以上是关于使用 knockout.js 和 select2 级联下拉菜单的主要内容,如果未能解决你的问题,请参考以下文章

带有自定义模板的 Knockout.Js 中的 Select2

Select2 与 Knockout.js 初始值

Knockout.js 与多个 Select2 绑定

Knockout.js Select2 绑定。将 Select2 升级到 v4 后损坏

Knockout/Select2:触发 select2 根据可观察的选项更新进行更新

在 Knockout.js 中异步应用绑定