knockoutjs复杂数组数据绑定在多个选择列表框中

时间:2012-05-23 07:51:29

标签: knockout.js complex-data-types

我在knockoutjs网站上找到了样本。这里它们是多重选择列表框的绑定值。但他们使用的是非常简单的可观察数组。 availableCountries selectedCountries

<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
    }
]

这里我的“ selectedCountries ”将是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数据没有得到更新。

有什么简单的方法吗?

提前致谢。

1 个答案:

答案 0 :(得分: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类,就像我在示例中所做的那样,这允许逻辑包含在descrete块中。我也不建议使用旧的jquery选择器,许多KO的初学者认为可以将两者混合使用。在我看来,它削弱了视图模型和视图之间的关注点分离。为什么在使用$(selector).click绑定时使用click

希望这有帮助。