Dans mon modèle de vue, j'ai une valeur IsMale qui a la valeur true ou false.
Dans mon interface utilisateur, je souhaite le lier aux boutons radio suivants:
<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>
Le problème que je pense est checked
attend une chaîne "vrai" / "faux". Ma question est donc la suivante: comment puis-je obtenir cette liaison bidirectionnelle avec cette interface utilisateur et ce modèle?
Réponses:
Une option consiste à utiliser une observable calculée inscriptible .
Dans ce cas, je pense qu'une bonne option est de faire de l'observable calculée inscriptible une "sous-observable" de votre
IsMale
observable. Votre modèle de vue ressemblerait à ceci: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 }); };
Vous le lieriez dans votre interface utilisateur comme:
<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>
Exemple: http://jsfiddle.net/rniemeyer/Pjdse/
la source
Je sais que c'est un vieux fil de discussion, mais j'avais le même problème et j'ai découvert une bien meilleure solution qui a probablement été ajoutée à KO après que cette question ait été officiellement répondue, alors je vais laisser cela aux personnes ayant le même problème.
Actuellement, il n'y a pas besoin d'extendeurs, de gestionnaires de liaison personnalisés ou de calculés. Fournissez simplement une option "checkedValue", il l'utilisera à la place de l'attribut html 'value', et avec cela, vous pouvez passer n'importe quelle valeur javascript.
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: true"/> <input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: false"/>
Ou:
<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"/>
la source
value
liaison puis lachecked
liaison (dans cet ordre), mais avec 3.0, je devais utiliser lacheckedValue
liaison au lieu de lavalue
liaison. (2) avant la v3.0 était difficile d'exiger que lavalue
liaison précède lachecked
liaison pour fonctionner correctement, j'ai donc le sentiment que les choses pourraient également mieux fonctionner dans la v3.0 dans tous les scénarios si vous mettez lacheckedValue
liaison avant lachecked
liaison, comme ils apparaissent dans les documents .checked
passeur (ou peut-être quelque chose qui en dépend) lève une erreur - alors la case à cocher correcte n'est pas cochéeCela fonctionne pour moi:
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>
la source
vm.IsMale(!!vm.+IsMale());
avant de sérialiser json pour l'envoyer au serveur (au cas où le côté serveur ne pourrait pas le gérer correctement)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); } } };
Utilisez ce classeur au lieu de créer des observables calculés par ko stupides.
Exemple:
<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>
la source
Une fois que vous avez compris que la correspondance initiale pour le bouton radio ne veut correspondre qu'à une chaîne et que vous souhaitez définir la valeur sur une chaîne, il s'agit simplement de convertir votre valeur initiale en chaîne. J'ai dû lutter contre cela avec les valeurs d'Int.
Après avoir configuré vos observables, convertissez la valeur en chaîne et KO fera sa magie à partir de là. Si vous mappez avec des lignes individuelles, effectuez la conversion dans ces lignes.
Dans l'exemple de code, j'utilise Json pour mapper l'ensemble du modèle en une seule commande. Laissez ensuite Razor insérer la valeur entre les guillemets pour la conversion.
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>
J'utilise un "Dump it all to the screen" en bas de ma page Web pendant le développement.
<h4>Debug</h4> <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
Voici les valeurs des données, avant
"OrderStatusID": 6, "ManifestEntered": true,
et, après
"OrderStatusID": "6", "ManifestEntered": "True",
Dans mon projet, je n'ai pas eu besoin de convertir des Bools, car je suis capable d'utiliser une case à cocher qui n'a pas la même frustration.
la source
Pourquoi pas simplement vrai et faux au lieu de 1 et 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>
la source
Vous pouvez également utiliser un extenseur afin qu'il soit facile de les réutiliser pour plus d'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; };
Ensuite, utilisez-le comme ceci:
this.IsMale.forEditing = this.IsMale.extend({boolForEditing: true});
Le paramètre fourni à
boolForEditing
indique si la valeur peut être nulle.Voir http://jsfiddle.net/G8qs9/1/
la source
Après avoir effectué de nombreuses recherches sur l'ancienne version de KO avant la version 3.0, il existe probablement deux meilleures options.
Créez un prolongateur knockout comme
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; };
Pour utiliser l'extension sur votre modèle, procédez comme suit:
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>
source: http://www.timlabonne.com/2013/02/building-a-knockout-js-extender-for-boolean-values/
la source