我们环境中的典型场景是允许用户选择服务器提供的选项列表(终端,产品......),然后显示请求的数据。
服务器提供的选项是名称,ID格式,因此以下的淘汰构造经常使用:
<select data-bind="options: serverOptions, optionsText: 'Name', optionsValue: 'ID', value: selectedOption>
最好制作一个自定义的bindingHandler,名为&#39; NamedIdOptions&#39;在allBindingsAccessor()上指定optionsText和optionsValue,然后重定向到标准选项绑定处理程序。
即
<select data-bind="NamedIdOptions: serverOptions, value: selectedOption"></select>
以前,我已经制作了自己的绑定处理程序,它填充了我自己的选项 - 但是,我更喜欢使用选项绑定处理程序提供的框架。
我尝试了不同的方法而没有太大的成功 - 选项绑定使用allBindings [&#39; optionsValue&#39;]和allBindings [&#39; optionsText&#39;]来访问该值,似乎我无法设置这些。 (我想避免使用applyBindingsToNode方法并写下以下内容:
ko.bindingHandlers.NamedIdOptions = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel)
{
var allBindings = allBindingsAccessor();
allBindings.*FORCESET*("optionsText", "Name");
allBindings.*FORCESET*("optionsValue", "ID");
retun ko.bindingHandlers.options.init.apply(this, arguments);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel)
{
retun ko.bindingHandlers.options.update.apply(this, arguments);
}
}
但是,似乎我无法在allBindings上设置任何内容。
我不允许使用
allBindings['_ko_property_writers']['optionsText']("Name" );
allBindings['_ko_property_writers']['optionsValue']("ID" );
我真的更愿意避免在init构造中将applyBindingsToNode作为
Knockout - is it possible to combine standard select bindings with a custom binding?
现在有人想解决这个问题的简单方法吗?
答案 0 :(得分:3)
您可以考虑使用ko.applyBindingAccessorsToNode。这就是我在KO 3.0中开始这样做的方式:
ko.bindingHandlers.NamedIdOptions = {
init: function(element, valueAccessor, allBindingsAccessor)
{
var injectedBindingValues = {
options: valueAccessor,
optionsValue: function () { return "ID" },
optionsText: function () { return "Name" }
};
ko.applyBindingAccessorsToNode(element, injectedBindingValues);
//tell Knockout that we have already handled binding the children of this element
//
return { controlsDescendantBindings: true };
}
}
您可以在this fiddle中看到它的实际效果。
注意:我通常使用从服务器(C#,JSON.NET)发送的JSON模式,从C#属性或数据库模式元数据中自动填充UI中的选项。我提炼了我的代码并对其进行了更改以匹配OP对问题的连续性所做的事情。但是,如果对JSON模式技术有任何兴趣,请点击我,我可以发布它。
答案 1 :(得分:2)
好的 - 我最后还是将节点绑定应用于节点:
ko.bindingHandlers.NamedIdOptions =
{
init: function (element, valueAccessor, allBindingsAccessor, viewModel)
{
var allBindings = allBindingsAccessor();
var newBindingOptions = { options: allBindings.NamedIdOptions, optionsText: "Name", optionsValue: "ID" };
delete allBindings.NamedIdOptions;
ko.utils.extend(newBindingOptions, allBindings);
ko.applyBindingsToNode(element, newBindingOptions, viewModel);
}
};
它似乎按预期工作 - 我对值和selectedOptions有点不确定 - 它们有'after'设置为选项。我想在绑定值之前对NamedIdOptions进行计划时我是安全的吗?
答案 2 :(得分:1)
转发呼叫时,你不能伪造整个allBindingsAccessor参数吗?
update: function (element, valueAccessor, allBindingsAccessor, viewModel)
{
var allBindings = allBindingsAccessor(),
fakeAllBindingsAccessor = function () {
// I've used jQuery.extend here, you could also manually add the properties to the allBindings object
return $.extend(true, allBindings, {
optionsValue: 'ID',
optionsText: 'Name'
};
};
return ko.bindingHandlers.options.init.call(this, element, valueAccessor, fakeAllBindingsAccessor, viewModel);
}
编辑:添加了一些代码,将现有的allBindingsAccessor与手工假绑定结合起来
答案 3 :(得分:0)
我最终得到了以下解决方案,它也允许制作简单的依赖过滤器 - 它使用下划线,但这只是为了方便:
// NamedIdOptions - is basically equal to the options binding - except, optionsText="Name", and "optionsValue='ID'"
// The options can be filered - Specifying optionsFilter: {'FilterProp' : 'valueToBeMatched', 'FilterProp2' : VMpropToMatch, .. }
// Definig optionsFilterCallback, registers a callback which will be invoked with the matched elements
// which can be used to turn off elements etc.
ko.bindingHandlers.NamedIdOptions =
{
init: function (element, valueAccessor, allBindingsAccessor, viewModel)
{
var allBindings = allBindingsAccessor(),
appliedValueAccesor = valueAccessor(),
shownOptions = appliedValueAccesor,
unwrap = ko.utils.unwrapObservable;
if (allBindings.optionsFilter)
{
shownOptions = ko.computed(function ()
{
// First - find all items to be presented in the list
var allItems = unwrap(appliedValueAccesor);
// Extract items to match against
// it is ensured that the computed observable dependts on all its sub properties
// All items are matched by key into an array
// if the match is null, undefined, or an empty array, it is not included int the match
var matchItems = {};
_.each(_.keys(allBindings.optionsFilter), function (key)
{
var observedValues = unwrap(allBindings.optionsFilter[key]);
if (!observedValues)
return;
if (!_.isArray(observedValues))
observedValues = [observedValues];
matchItems[key] = observedValues;
});
// Find items that match the items above - uses ko own routine to do so
var matchedItems = _.filter(allItems, function (elm)
{
return _.all(_.keys(matchItems), function (key)
{
var match = _.contains(matchItems[key], elm[key]);
return match;
});
});
// if a callback is defined - call it with the matched items
if (allBindings.optionsFilterCallback)
allBindings.optionsFilterCallback(matchedItems);
return matchedItems;
}, this);
}
// Change the binding options - the already handled items should not be reapplied to the node
// NOTE: it would be preferable to use 'ko.3.0->applyBindingAccessorsToNode' instead of the hack below
// It assumes that the order of dictionaries are not changed - it works, but is not complient with the Ecmascript standard
var newBindingOptions = { options: shownOptions, optionsText: "Name", optionsValue: "ID" };
var bindingKeys = _.keys(allBindings);
var handledItems = _.first(bindingKeys, _.indexOf(bindingKeys, "NamedIdOptions") + 1);
_.each(handledItems, function (item)
{
delete allBindings[item];
});
_.extend(newBindingOptions, allBindings);
ko.applyBindingsToNode(element, newBindingOptions, viewModel);
}
};