web-dev-qa-db-fra.com

Liaison vrai / faux aux boutons radio de Knockout JS

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 "true"/"false". Ma question est donc la suivante: comment puis-je obtenir cette liaison bidirectionnelle avec cette interface utilisateur et ce modèle?

82
C.J.

Une option consiste à utiliser un observable calculé en écriture .

Dans ce cas, je pense qu’une option intéressante est de faire de l’observable calculée en écriture un "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/

79
RP Niemeyer

Je sais que c’est un vieux fil, mais j’avais le même problème et j’ai découvert une solution bien meilleure qui a probablement été ajoutée à KO après que la question ait été officiellement résolue. Je laisserai donc cela aux personnes ayant le même problème.

Actuellement, il n'est pas nécessaire d'utiliser des extensions, des gestionnaires de liaison personnalisés ou des calculs. Fournissez simplement une option "checkedValue", il l'utilisera au lieu de l'attribut html "value" et vous permettra ainsi de transmettre toute 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"/>
128
Natan

Cela 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>
10
Artem
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 de stupides ko observables calculés.

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>
1
KurtJ

Une fois que vous avez compris que la correspondance initiale pour le bouton radio veut seulement faire correspondre une chaîne et veut définir la valeur comme chaîne, il s’agit simplement de convertir votre valeur initiale en string. Je devais me battre avec les valeurs Int.

Une fois que vous avez 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 le modèle entier en une seule commande. Ensuite, laissez 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 "Vider tout à l'écran" au 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 de données, Before

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

et, Après

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

Dans mon projet, je n'avais pas besoin de convertir Bools, car je pouvais utiliser une case à cocher qui n'avait pas la même frustration.

1
Greg Little

Vous pouvez également utiliser un extender 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 null.

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

0
sroes

Pourquoi ne 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>
0
Doctor

Après avoir effectué beaucoup de recherches sur l’ancienne version de Knockout antérieure à la 3.0, il existe probablement deux meilleures options.

Créez un prolongateur knock-out 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’extendeur 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/

0
Anshul Nigam