使用 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: <span data-bind="text: selectedMake"></span>
而不是使用方括号,因为您不会尝试访问可观察对象的属性。
这又是小提琴:http://jsfiddle.net/jiggle/Lw2qJ/
希望对你有帮助。
【讨论】:
以上是关于使用 knockout.js 和 select2 级联下拉菜单的主要内容,如果未能解决你的问题,请参考以下文章
带有自定义模板的 Knockout.Js 中的 Select2
Knockout.js Select2 绑定。将 Select2 升级到 v4 后损坏