Bootstrap-multiselect: KnockoutJS-Bindung an Objektwert (kein einfacher Wert) ... Hack-Fix enthalten

Erstellt am 2. Mai 2015  ·  3Kommentare  ·  Quelle: davidstutz/bootstrap-multiselect

Ich verwende die Knockout-Auswahlbindung, bei der ein gesamtes Objekt als Wert gebunden wird ... nicht nur eine Zeichenfolge.

Bootstrap-multiselect mochte dies nicht, weil es den "Wert" verwendet, um seine Dropdown-Listenelemente mit der entsprechenden Auswahloption zu verknüpfen. Dazu müssen Sie in Ihrer Knockout-Bindung das Attribut "optionsValue" gesetzt haben, damit die Option ein "value"-Attribut hat. Das Festlegen des "optionsValue"-Attributs hat dann jedoch das erwartete Ergebnis, dass nur dieser Wert in meinem selectedOptions-observableArray festgelegt wird ... nicht das gesamte Objekt.

Es ist gut möglich, dass ich die einzige Person bin, die die Funktionalität von Knockout verwendet, mit der Sie die Optionen an ganze Objekte binden können, aber für den Fall, dass ich es nicht bin ... hier ist der Hack-Fix:

Es wurden zwei neue Bootstrap-Multiselect-Optionen namens "optionsKey" und "observableKey" erstellt, damit sie weiß, wo sie einen Wert finden kann, auf den sie sich beziehen kann:

<select id="mySelect" data-bind="options: myOptions, selectedOptions: mySelectedOptions, optionsText: 'myDisplayText', optionsAfterRender: SetOptionKey, multiselect: { optionsKey: 'data-key', observableKey: 'Key' }" multiple="multiple"></select>

In meinem Knockout-Ansichtsmodell setze ich das Attribut, auf das von "optionsKey" verwiesen wird, für jede Option:

myVM.SetOptionKey = function ( option, item ) {
    ko.applyBindingsToNode( option, { attr: { "data-key": item.Key } }, item );
};

Jetzt tauche ich in die Datei bootstrap-multiselect.js ein. Das erste, was ich hier tun musste, ist oben, wo der Knockout-Init-Handler hinzugefügt wird.

init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
    ...
    if (allBindings.has('selectedOptions')) {
        var selectedOptions = allBindings.get('selectedOptions');
        if (ko.isObservable(selectedOptions)) {
            ko.computed({
                read: function () {
                    selectedOptions();

                    // Added to handle knockout binding to an object...not just a value
                    // multiselect needs the selected property on the options, else it wont show them on refresh
                    if ((config.optionsKey !== undefined) &&
                        (config.optionsKey !== null) &&
                        (config.optionsKey.length > 0) &&
                        (config.observableKey !== undefined) &&
                        (config.observableKey !== null) &&
                        (config.observableKey.length > 0)) {
                        var _optionsKey = config.optionsKey;
                        var _observableKey = config.observableKey;
                        ko.utils.arrayForEach(selectedOptions(), function (selectedOption) {
                            $("option[" + _optionsKey + "='" + selectedOption[_observableKey]() + "']", $element).prop('selected', true);
                        })
                    }

                    setTimeout(function () {
                        $element.multiselect('refresh');
                    }, 1);
                },
                disposeWhenNodeIsRemoved: element
            }).extend({ rateLimit: 100, notifyWhenChangesStop: true });
        }
    }
    ...

Als nächstes musste ich die createOptionValue-Funktion in bootstrap-multiselect.js ändern, um diesen Schlüssel zuerst als den Wert zu verwenden, den er in seinen Listenelementen festlegt:

createOptionValue: function (element) {
    ...

    var value = $element.val();
    // Added to handle knockout binding to an object...not just a value
    if ((this.options.optionsKey !== undefined) && (this.options.optionsKey !== null) && (this.options.optionsKey.length > 0)) {
        var key = $element.attr(this.options.optionsKey);
        if ((key !== undefined) && (key !== null) && (key.length > 0)) {
            value = key;
        }
    }

    ...
}

Zuletzt musste ich die Funktion getOptionByValue in bootstrap-multiselect.js ändern, um Optionselemente basierend auf diesem Attribut anstelle des Wertes zu finden:

getOptionByValue: function (value) {
    var valueToCompare = value.toString();

    // Added to handle knockout binding to an object...not just a value
    if ((this.options.optionsKey !== undefined) && (this.options.optionsKey !== null) && (this.options.optionsKey.length > 0)) {
        var opt = $("option[" + this.options.optionsKey + "='" + valueToCompare + "']", this.$select).first();
        if( (opt !== undefined) && (opt !== null) ) {
            return $(opt);
        }
    }

    var options = $('option', this.$select);

    for (var i = 0; i < options.length; i = i + 1) {
        var option = options[i];
        if (option.value === valueToCompare) {
            return $(option);
        }
    }
},
question

Alle 3 Kommentare

Danke, werde dies in die FAQ aufnehmen! Ich bin sicher, dass dies auch für andere Benutzer hilfreich ist.

Vielen Dank für diesen Tipp - ich habe versucht, auch die Bindung an ausgewählte Objekte zu erreichen, und diese Beschreibung war sehr hilfreich. Das einzige, was ich zusätzlich zum Code von @APM3 tun musste, ist auch das "value" -Attribut meiner Optionen in der SetOptionKey-Funktion zu füllen:

self.SetOptionKey = function (option, item) { ko.applyBindingsToNode(option, { attr: { "data-key": item.Value } }, item); ko.applyBindingsToNode(option, { attr: { "value": item.Value } }, item); }

Nochmals vielen Dank für dieses Schreiben.

Siehe Gist für die Knockout-Bindung dieses unterstützten Objekts. Ersetzen Sie einfach die tatsächliche Bindung aus der Datei bootstrap-multiselect.js. In diesem Code müssen Sie die Option nicht in Ihre VM eingeben.

//Just like this one
myVM.SetOptionKey = function ( option, item ) {
    ko.applyBindingsToNode( option, { attr: { "data-key": item.Key } }, item );
};

Ich habe den Bindungen eine Vorverarbeitung hinzugefügt, um implizit optionsAfterRender hinzuzufügen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen