Binden von wahr / falsch an Optionsfelder in Knockout JS

83

In meinem Ansichtsmodell habe ich einen IsMale-Wert mit dem Wert true oder false.

In meiner Benutzeroberfläche möchte ich es an die folgenden Optionsfelder binden:

<label>Male
   <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale"/>
</label>

Ich denke, das Problem ist, checkeddass eine Zeichenfolge "true" / "false" erwartet wird. Meine Frage ist also, wie kann ich diese 2-Wege-Bindung mit dieser Benutzeroberfläche und diesem Modell erhalten?

CJ
quelle
10
Für Knockout-Versionen> = 3.0 finden Sie in Natans Antwort eine einfachere Lösung als in der akzeptierten Antwort vorgeschlagen.
Markus Pscheidt

Antworten:

80

Eine Möglichkeit besteht darin, eine beschreibbare berechnete beobachtbare Größe zu verwenden .

In diesem Fall denke ich, dass eine gute Option darin besteht, das beschreibbare berechnete Observable zu einem "Sub-Observable" Ihres IsMaleObservable zu machen. Ihr Ansichtsmodell würde folgendermaßen aussehen:

var ViewModel = function() {
   this.IsMale = ko.observable(true);

   this.IsMale.ForEditing = ko.computed({
        read: function() {
            return this.IsMale().toString();  
        },
        write: function(newValue) {
             this.IsMale(newValue === "true");
        },
        owner: this        
    });          
};

Sie würden es in Ihrer Benutzeroberfläche wie folgt binden:

<label>Male
   <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale.ForEditing"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale.ForEditing"/>
</label>

Beispiel: http://jsfiddle.net/rniemeyer/Pjdse/

RP Niemeyer
quelle
Scheint eine großartige Lösung zu sein. Ich verwende das Mapping-Pluggin, um meine VM zu aktualisieren. Wissen Sie, dass ForEditing auf IsMale ausgelöscht werden würde?
CJ
26
Hier ist eine Alternative, die die berechnete beobachtbare Erstellung in eine benutzerdefinierte Bindung umwandelt
RP Niemeyer
2
+1 für die Verwendung einer Bindung, anstatt für jedes Objekt ein Observable zu erstellen
Greg Ennis
1
@RPNiemeyer ist definitiv der richtige Weg.
Andrew
1
@RPNiemeyer danke! Diese Geige in den Kommentaren hat für mich funktioniert.
SFlagg
128

Ich weiß, dass dies ein alter Thread ist, aber ich hatte das gleiche Problem und fand eine viel bessere Lösung heraus, die wahrscheinlich zum Knockout hinzugefügt wurde, nachdem diese Frage offiziell beantwortet wurde, also überlasse ich es einfach Leuten mit dem gleichen Problem.

Derzeit sind keine Extender, benutzerdefinierten Bindungshandler oder berechneten Daten erforderlich. Geben Sie einfach eine "checkedValue" -Option an, die anstelle des HTML-Attributs "value" verwendet wird. Damit können Sie einen beliebigen Javascript-Wert übergeben.

<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: true"/>
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: false"/>

Oder:

<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 1"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 2"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 3"/>
Natan
quelle
2
Wenn man sich die Quelle ansieht, scheint es eine Entscheidung zu treffen, den geprüften Wert hier zu verwenden . Der 'useCheckedValue' wird auf true gesetzt, wenn der Eingang ein Radio oder ein Kontrollkästchen mit dem Wert als Array ist . Außerdem verwende ich Knockout 3.0. Sehen Sie, ob das hilft.
Natan
1
Ja, danke, ich habe ein Upgrade auf 3.0.0 durchgeführt und jetzt ist es da. Ich muss meine booleschen Werte immer noch in einen String
einschließen,
Gute Infos. Zwei zusätzliche Punkte: (1) Ich habe festgestellt, dass ich mit Knockout vor Version 3.0 die valueBindung und dann die checkedBindung (in dieser Reihenfolge) verwenden konnte, aber mit 3.0 musste ich die checkedValueBindung anstelle der valueBindung verwenden. (2) Pre-v3.0 war wählerisch, wenn es darum ging, dass die valueBindung der Bindung vorausgeht checked, damit sie ordnungsgemäß funktioniert. Daher habe ich das Gefühl, dass die Dinge in v3.0 in allen Szenarien möglicherweise auch besser funktionieren, wenn Sie die checkedValueBindung vor die checkedBindung stellen, z Sie werden in den Dokumenten angezeigt .
Jason Frank
@ ZiglioNZ Eine Möglichkeit, wie dies fehlschlagen kann, ist, wenn Ihr checkedSetter (oder möglicherweise etwas, das davon abhängt) einen Fehler
auslöst
Ich verwende Version 3.4 und stelle fest, dass ich immer noch value = "true" und das oben Gesagte eingeben muss, damit es funktioniert.
Wirble
11

Das funktioniert bei mir:

http://jsfiddle.net/zrBuL/291/

<label>Male
   <input type="radio" name="IsMale" value="1" data-bind="checked:IsMale"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="0" data-bind="checked:IsMale"/>
</label>
Artem
quelle
Das einzige Problem, das ich sehe, ist, dass beim Serialisieren des Ansichtsmodells für die Übergabe an den Server Ganzzahlen in Observable (anstelle von Booleschen Werten) angezeigt werden. Sie müssen anrufen, vm.IsMale(!!vm.+IsMale());bevor Sie json serialisieren, um es an den Server zu senden (falls die Serverseite nicht richtig damit umgehen kann)
Artem
Ich denke, Sie haben Recht, aber meine Javascript-Kenntnisse fehlen. Können Sie mir bitte erklären, wie !! + funktioniert? Ich bin mit dieser Syntax nicht vertraut.
CJ
1
@CJ dies konvertiert Zeichenfolge oder Zahl in boolean - chek this jsfiddle.net/nickolsky/6ydLZ , wenn Zeichenfolge bereits bool ist, wird es als bool beibehalten
Artem
Vielen Dank. Ich denke, das ist auch eine großartige Lösung.
CJ
1
Es funktioniert jedoch nicht in beide Richtungen. Die Optionsfelder werden beim Laden nicht aktiviert.
Mikael Östberg
1
ko.bindingHandlers['radiobuttonyesno'] = {
    'init': function (element, valueAccessor, allBindingsAccessor) {
        var stateHandler = function (property, allBindingsAccessor, key, value, checkIfDifferent) {
            if (!property || !ko.isObservable(property)) {
                var propWriters = allBindingsAccessor()['_ko_property_writers'];
                if (propWriters && propWriters[key])
                    propWriters[key](value);
            } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
                property(value);
            }
        };

        var updateHandler = function () {
            var valueToWrite;

            if ((element.type == "radio") && (element.checked)) {
                valueToWrite = element.value;
            } else {
                return; // "radiobuttonyesno" binding only responds to selected radio buttons
            }

            valueToWrite = (valueToWrite === "True") ? true : false;

            var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue); //can be true of false

            stateHandler(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
        };
        ko.utils.registerEventHandler(element, "click", updateHandler);

        // IE 6 won't allow radio buttons to be selected unless they have a name
        if ((element.type == "radio") && !element.name)
            ko.bindingHandlers['uniqueName']['init'](element, function () { return true });
    },
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());

        value = value ? "True" : "False";

        if (element.type == "radio") {
            element.checked = (element.value == value);
        }
    }
};

Verwenden Sie diesen Ordner, anstatt dumme ko-berechnete Observablen zu erstellen.

Beispiel:

<label>Male
        <input type="radio" name="IsMale" value="True" data-bind="radiobuttonyesno:IsMale"/>
     </label> 
     <label>Female
        <input type="radio" name="IsMale" value="False" data-bind="radiobuttonyesno:IsMale"/>
     </label>
KurtJ
quelle
1

Sobald Sie herausgefunden haben, dass die anfängliche Übereinstimmung für das Optionsfeld nur mit einer Zeichenfolge übereinstimmen und den Wert auf eine Zeichenfolge setzen möchte, müssen Sie lediglich Ihren Anfangswert in eine Zeichenfolge konvertieren. Ich musste dies mit Int-Werten bekämpfen.

Nachdem Sie Ihre Observablen eingerichtet haben, konvertieren Sie den Wert in einen String, und KO wird von dort aus seine Magie entfalten. Wenn Sie einzelne Zeilen zuordnen, führen Sie die Konvertierung in diesen Zeilen durch.

Im Beispielcode verwende ich Json, um das gesamte Modell in einem einzigen Befehl abzubilden. Lassen Sie dann Razor den Wert zwischen den Anführungszeichen für die Konvertierung einfügen.

script type="text/javascript">
    KoSetup.ViewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));
    KoSetup.ViewModel.ManifestEntered("@Model.ManifestEntered");       //Bool
    KoSetup.ViewModel.OrderStatusID("@Model.OrderStatusID");           //Int
</script>

Ich verwende während der Entwicklung unten auf meiner Webseite ein "Alles auf den Bildschirm werfen".

<h4>Debug</h4>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

Hier sind die Datenwerte, Vorher

"OrderStatusID": 6,
"ManifestEntered": true,

und danach

"OrderStatusID": "6",
"ManifestEntered": "True",

In meinem Projekt musste ich Bools nicht konvertieren, da ich ein Kontrollkästchen verwenden kann, das nicht die gleiche Frustration aufweist.

Greg Little
quelle
0

Warum nicht einfach wahr und falsch statt 1 und 0?

 <label>Male
    <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale"/>
 </label> 
 <label>Female
    <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale"/>
 </label>
Arzt
quelle
2
Wenn Sie JSON-Objekte senden, macht dies einen Unterschied.
Doktor
0

Sie können auch einen Extender verwenden, damit Sie ihn problemlos für weitere Observable wiederverwenden können:

ko.extenders.boolForEditing = function (target, allowNull) {
    var result = ko.computed({
        read: function () {
            var current = target();
            var newValue = null;
            if (current === undefined || current === null || current === '') {
                if (!allowNull) {
                    newValue = 'false';
                }
            } else {
                newValue = current ? 'true' : 'false';
            }
            return newValue;
        },
        write: function (newValue) {
            var current = target();
            var valueToWrite = null;
            if (newValue === undefined || newValue === null || newValue === '') {
                if (!allowNull) {
                    valueToWrite = false;
                }
            } else {
                valueToWrite = newValue === 'true';
            }
            // only write if it changed
            if (valueToWrite !== current) {
                target(valueToWrite);
            } else {
                if (newValue !== current) {
                    target.notifySubscribers(valueToWrite);
                }
            }
        }
    }).extend({
        notify: 'always'
    });

    result(target());

    return result;
};

Dann benutze es so:

this.IsMale.forEditing = this.IsMale.extend({boolForEditing: true});

Der angegebene Parameter boolForEditinggibt an, ob der Wert null sein darf.

Siehe http://jsfiddle.net/G8qs9/1/

sroes
quelle
0

Nachdem Sie vor 3.0 viel nach älteren Versionen von Knockout gesucht haben, gibt es möglicherweise zwei beste Optionen

Erstellen Sie einen Knockout-Extender wie

ko.extenders["booleanValue"] = function (target) {
    target.formattedValue = ko.computed({
        read: function () {
            if (target() === true) return "True";
            else if (target() === false) return "False";
        },
        write: function (newValue) {
            if (newValue) {
                if (newValue === "False") target(false);
                else if (newValue === "True") target(true);
            }
        }
    });

    target.formattedValue(target());
    return target;
};

Um den Extender für Ihr Modell zu verwenden, gehen Sie wie folgt vor:

function Order() {
  this.wantsFries= ko.observable(false).extend({ booleanValue: null });
}

<span>Do you want fries with that?</span>
<label>
  <input type="radio" name="question" value="True"
             data-bind="value: wantsFries.formattedValue" /> Yes
</label>
<label>
  <input type="radio" name="question" value="False"
             data-bind="value: wantsFries.formattedValue" /> No
</label>

Quelle: http://www.timlabonne.com/2013/02/building-a-knockout-js-extender-for-boolean-values/

Anshul Nigam
quelle