En mi modelo de vista, tengo un valor IsMale que tiene el valor verdadero o falso.
En mi interfaz de usuario, deseo vincularlo a los siguientes botones de opción:
<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>
Creo que el problema es que checked
espera una cadena "verdadero" / "falso". Entonces, mi pregunta es, ¿cómo puedo obtener este enlace bidireccional con esta interfaz de usuario y modelo?
Respuestas:
Una opción es utilizar un observable calculado que se pueda escribir .
En este caso, creo que una buena opción es hacer que el observable calculado que se puede escribir sea un "subaobservable" de su
IsMale
observable. Su modelo de vista se vería así: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 }); };
Lo vincularía en su interfaz de usuario como:
<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>
Muestra: http://jsfiddle.net/rniemeyer/Pjdse/
fuente
Sé que este es un hilo antiguo, pero estaba teniendo el mismo problema y descubrí una solución mucho mejor que probablemente se agregó al nocaut después de que esta pregunta fuera respondida oficialmente, así que lo dejaré para las personas con el mismo problema.
Actualmente no hay necesidad de extensores, manejadores de enlace personalizados o computados. Simplemente proporcione una opción "checkValue", la usará en lugar del atributo html 'value', y con eso puede pasar cualquier valor javascript.
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: true"/> <input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: false"/>
O:
<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"/>
fuente
value
enlace y luego elchecked
enlace (en ese orden), pero con 3.0 tuve que usar elcheckedValue
enlace en lugar delvalue
enlace. (2) pre-v3.0 era exigente al requerir que elvalue
enlace preceda alchecked
enlace para que funcione correctamente, así que tengo la sensación de que las cosas también podrían funcionar mejor en v3.0 en todos los escenarios si coloca elcheckedValue
enlace antes delchecked
enlace, como se muestran en los documentos .checked
configurador (o tal vez algo que depende de él) genera un error, entonces la casilla de verificación correcta no está marcadaEsto funciona para mi:
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>
fuente
vm.IsMale(!!vm.+IsMale());
antes de serializar json para enviarlo al servidor (en caso de que el lado del servidor no pueda manejarlo correctamente)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); } } };
Utilice esta carpeta en lugar de crear observables calculados ko estúpidos.
Ejemplo:
<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>
fuente
Una vez que se dé cuenta de que la coincidencia inicial para el botón de opción solo quiere hacer coincidir una cadena y desea establecer el valor en una cadena, es simplemente una cuestión de convertir su valor inicial en una cadena. Tuve que luchar contra esto con los valores de Int.
Una vez que haya configurado sus observables, convierta el valor en una cadena y KO hará su magia desde allí. Si está mapeando con líneas individuales, realice la conversión en esas líneas.
En el código de ejemplo, estoy usando Json para mapear todo el modelo en un solo comando. Luego, dejando que Razor inserte el valor entre las comillas para la conversión.
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>
Utilizo un "Volcarlo todo a la pantalla" en la parte inferior de mi página web durante el desarrollo.
<h4>Debug</h4> <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
Aquí están los valores de los datos, antes
"OrderStatusID": 6, "ManifestEntered": true,
y, después
"OrderStatusID": "6", "ManifestEntered": "True",
En mi proyecto, no necesitaba convertir Bools, porque puedo usar una casilla de verificación que no tiene la misma frustración.
fuente
¿Por qué no simplemente verdadero y falso en lugar de 1 y 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>
fuente
También puede usar un extensor para que sea fácil reutilizarlos para más observables:
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; };
Entonces úsalo así:
this.IsMale.forEditing = this.IsMale.extend({boolForEditing: true});
El parámetro proporcionado para
boolForEditing
indica si el valor puede ser nulo.Ver http://jsfiddle.net/G8qs9/1/
fuente
Después de investigar mucho para la versión anterior de knockout anterior a la 3.0, posiblemente haya dos mejores opciones
Crea un extensor de knockout como
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; };
Para usar el extensor en su modelo, haría algo como lo siguiente:
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>
fuente: http://www.timlabonne.com/2013/02/building-a-knockout-js-extender-for-boolean-values/
fuente