【问题标题】:knockoutjs complex array databind in multiple select listbox多选列表框中的knockoutjs复杂数组数据绑定
【发布时间】:2012-05-23 07:51:28
【问题描述】:

我在 knockoutjs 网站上找到了示例。在这里,它们将值绑定到多选列表框。但他们使用的是非常简单的可观察数组。 availableCountrieschosenCountries

<p>
    Choose some countries you'd like to visit:
    <select data-bind="options: availableCountries, selectedOptions: chosenCountries" size="5" multiple="true"></select>
</p>

<script type="text/javascript">
    var viewModel = {
        availableCountries : ko.observableArray(['France', 'Germany', 'Spain']),
        chosenCountries : ko.observableArray(['Germany']) // Initially, only Germany is selected
    };

    // ... then later ...
    viewModel.chosenCountries.push('France'); // Now France is selected too
</script>

但是我的模型太复杂了,提到我模型的部分是json格式。

"JobOrderDelivTranscript" : [{
        "TranscriptType" : {
            "Id" : 1,
            "Name" : null,
            "CreatedBy" : 0,
            "CreatedDate" : "0001-01-01T00:00:00",
            "ModifiedBy" : 0,
            "ModifiedDate" : "0001-01-01T00:00:00",
            "IsActive" : false,
            "EntityStatus" : 0,
            "ErrorMessage" : null,
            "ExternalID" : 0,
            "ExternalSystemID" : 0
        },
        "Id" : 1,
        "Name" : null,
        "CreatedBy" : 0,
        "CreatedDate" : "0001-01-01T00:00:00",
        "ModifiedBy" : 0,
        "ModifiedDate" : "0001-01-01T00:00:00",
        "IsActive" : false,
        "EntityStatus" : 0,
        "ErrorMessage" : null,
        "ExternalID" : 0,
        "ExternalSystemID" : 0
    }, {
        "TranscriptType" : {
            "Id" : 2,
            "Name" : null,
            "CreatedBy" : 0,
            "CreatedDate" : "0001-01-01T00:00:00",
            "ModifiedBy" : 0,
            "ModifiedDate" : "0001-01-01T00:00:00",
            "IsActive" : false,
            "EntityStatus" : 0,
            "ErrorMessage" : null,
            "ExternalID" : 0,
            "ExternalSystemID" : 0
        },
        "Id" : 2,
        "Name" : null,
        "CreatedBy" : 0,
        "CreatedDate" : "0001-01-01T00:00:00",
        "ModifiedBy" : 0,
        "ModifiedDate" : "0001-01-01T00:00:00",
        "IsActive" : false,
        "EntityStatus" : 0,
        "ErrorMessage" : null,
        "ExternalID" : 0,
        "ExternalSystemID" : 0
    }
]

这里我的“chosenCountries”将是 JobOrderDelivTranscript()。如果我选择第一个选项,它应该与 JobOrderDelivTranscript()[0].TranscriptType.Id 映射。在他们的示例中,他们使用的是字符串数组,但我必须绑定复杂的数据。我该怎么做。

即使我尝试使用自定义绑定

ko.bindingHandlers['selectedCustomOptions'] = {
            getSelectedValuesFromSelectNode: function (selectNode) {
                var result = [];
                var nodes = selectNode.childNodes;
                for (var i = 0, j = nodes.length; i < j; i++) {
                    var node = nodes[i], tagName = ko.utils.tagNameLower(node);
                    if (tagName == "option" && node.selected)
                        result.push(ko.selectExtensions.readValue(node));
                    else if (tagName == "optgroup") {
                        var selectedValuesFromOptGroup = ko.bindingHandlers['selectedCustomOptions'].getSelectedValuesFromSelectNode(node);
                        Array.prototype.splice.apply(result, [result.length, 0].concat(selectedValuesFromOptGroup)); // Add new entries to existing 'result' instance
                    }
                }
                return result;
            },
            'init': function (element, valueAccessor, allBindingsAccessor) {
                ko.utils.registerEventHandler(element, "change", function () {
                    var value = valueAccessor();
                    var valueToWrite = ko.bindingHandlers['selectedCustomOptions'].getSelectedValuesFromSelectNode(this);
                    ko.jsonExpressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
                });
            },
            'update': function (element, valueAccessor) {
                if (ko.utils.tagNameLower(element) != "select")
                    throw new Error("values binding applies only to SELECT elements");

                var newValue = ko.utils.unwrapObservable(valueAccessor());
                if (newValue && typeof newValue.length == "number") {
                    var nodes = element.childNodes;
                    for (var i = 0, j = nodes.length; i < j; i++) {
                        var node = nodes[i];
                        if (ko.utils.tagNameLower(node) === "option")
                            ko.utils.setOptionNodeSelectionState(node, arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0);
                    }
                }
            }
        };

        function arrayIndexOf (array, item) {
            if (typeof Array.prototype.indexOf == "function")
                return Array.prototype.indexOf.call(array, item);
            for (var i = 0, j = array.length; i < j; i++)
                if (array[i].TranscriptType.Id() === item.Id)
                    return i;
            return -1;
        }

我已经选择了选项,但 json 数据没有得到更新。

有什么简单的方法吗?

提前致谢。

【问题讨论】:

    标签: knockout.js complex-data-types


    【解决方案1】:

    我不是 100% 我理解您的问题,但您似乎正在尝试使用 selectedOptions 绑定绑定到复杂对象。有两种方法可以做你想做的事。第一种是结合使用 optionsValue 绑定和计算来将您的绑定 id 拉到对象根级别(不幸的是,optionsValue 绑定仅适用于根,因此optionsValue: 'TranscriptType.Id' 不起作用)。

    <p>Choose some countries you'd like to visit:</p>
    <select data-bind="options: availableCountries, optionsText: optionsText, 
          optionsValue: 'id', selectedOptions: 
          chosenCountries" size="5" multiple="true"></select>
    
    <p data-bind="text: ko.toJSON(chosenCountries)">
    </p>
    
    var JobOrderDelivTranscript = function(id) {
        var self = this;
        this.TranscriptType = {
            Id : id
        }
        this.id = ko.computed(function() {
            return self.TranscriptType.Id
        });
    };
    

    http://jsfiddle.net/madcapnmckay/6K6kH/

    第二种方法是不使用optionsValue,在这种情况下KO将使用对象引用来测试相等性。只要您在您的 selectedCounties 数组中保留相同的对象引用,一切都会正常工作。

    var viewModel = function () {
        var self = this;
        this.availableCountries = ko.observableArray([
            new JobOrderDelivTranscript("Some Transcript 1"),
            new JobOrderDelivTranscript("Some Transcript 2"),
            new JobOrderDelivTranscript("Some Transcript 3")]);
    
        this.chosenCountries = ko.observableArray([ self.availableCountries()[0] ]);
    
        this.optionsText = function(option) {
            return option.TranscriptType.Id;
        };        
    };
    
    var vm = new viewModel();
    vm.chosenCountries.push(vm.availableCountries()[1]);
    

    http://jsfiddle.net/madcapnmckay/hmsqf/

    这两种方式各有利弊,这取决于你的具体情况是正确的。

    编辑

    要对映射插件执行相同操作,您需要使用文档here under "Advanced Usage" 中介绍的映射选项。

    这是一个你应该能够适应的例子。

    http://jsfiddle.net/madcapnmckay/hmsqf/2/

    为了更好地构建您的代码,我建议创建许多 javascript 类,就像我在 Order 示例中所做的那样,这允许将逻辑包含在离散块中。我也不建议使用旧的 jquery 选择器,许多 KO 初学者认为可以将两者混合使用。在我看来,它稀释了视图模型和视图之间的关注点分离。既然可以使用click 绑定,为什么还要使用$(selector).click

    希望这会有所帮助。

    【讨论】:

    • 感谢您的快速回复。请在 jsfiddle [link]jsfiddle.net/nanda/uVjUv/1 中找到我的示例应用程序。我正在使用映射模型。我有多个工作订单,每个订单都有列表框。我必须绑定列表框。再次感谢
    • @Nanda - 我的回答对你有帮助吗?我真的不明白你的jsfiddle发生了什么,你能把它简化一下。我想说,如果你想用映射插件做我展示的事情,你可能应该使用一些映射选项来正确地塑造你的数据。
    • 你的回答真的帮助了我。我对淘汰赛高级绑定有了更多的了解。如果我使用手动视图模型,我可以遵循您的代码。但是我正在使用映射插件,并且我是淘汰赛和映射的初学者。所以我无法使用高级选项。但是我会尝试。关于映射的任何想法,请分享您的想法。非常感谢你 madcapnmckay。
    • @Nanda - 如果这回答了您的问题,请接受答案。问候。
    猜你喜欢
    • 2012-11-30
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多