首页 文章

如何使用预定义的文本和值选项定义自定义挖空'options binding'

提问于
浏览
3

我们环境中的典型场景是允许用户选择服务器提供的选项列表(终端,产品......),然后显示请求的数据 .

服务器提供的选项是Name,ID格式,因此以下的knockout构造经常使用:

<select data-bind="options: serverOptions, optionsText: 'Name', optionsValue: 'ID', value: selectedOption>

最好在allBindingsAccessor()上创建一个名为'NamedIdOptions'的自定义bindingHandler,指定optionsText和optionsValue,然后重定向到标准选项绑定处理程序 .

<select data-bind="NamedIdOptions: serverOptions, value: selectedOption"></select>

以前,我已经制作了自己的绑定处理程序,它填充了我自己的选项 - 但是,我更喜欢使用选项绑定处理程序提供的框架 .

我尝试了不同的方法而没有太大的成功 - 选项绑定使用allBindings ['optionsValue']和allBindings ['optionsText']来访问该值,似乎我无法设置这些 . (我想避免使用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?

现在有人关于这个问题的简单解决方案吗?

4 回答

  • 1

    您可以考虑使用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模式技术有任何兴趣,那就打击我吧,我可以发布它 .

  • 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时我是安全的吗?

  • 0

    转发呼叫时,你不能伪造整个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

    我最终得到了以下解决方案,它也允许制作简单的dependendt过滤器 - 它使用下划线,但这只是为了方便:

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

相关问题