0

knockoutjs の Web サイトでサンプルを見つけました。ここでは、値を複数選択リスト ボックスにバインドしています。しかし、彼らは非常に単純な観測可能な配列を使用しています。 availableCountriesselectedCountries

<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 データが更新されませんでした。

簡単な方法はありますか?

前もって感謝します。

4

1 に答える 1

1

私は100%ではありませんが、あなたの質問は理解していますが、selectedOptionsバインディングを使用して複雑なオブジェクトにバインドしようとしているようです。あなたがやりたいことをする2つの方法があります。1つ目は、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/

2番目の方法は、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/

どちらの方法にも長所と短所があり、適切な状況によって異なります。

編集

マッピングプラグインで同じことを行うには、ここの「高度な使用法」のドキュメントで説明されているマッピングオプションを使用する必要があります。

これはあなたが適応できるはずの例です。

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

コードをより適切に構造化するために、Orderの例のように、多くのjavascriptクラスを作成することをお勧めします。これにより、ロジックをディスクリートブロックに含めることができます。また、古いjqueryセレクターを使用することはお勧めしません。KOの初心者の多くは、この2つを組み合わせても問題ないと考えています。私の意見では、それはあなたのビューモデルとビューの間の関心の分離を薄めています。バインディング$(selector).clickを使用できるのに、なぜを使用するのですか。click

お役に立てれば。

于 2012-05-23T17:34:54.973 に答える