Liaison true/false aux boutons radio dans Knockout JS


80

Dans mon modèle de vue, j'ai une valeur IsMale qui a la valeur true ou false.

Dans mon interface je tiens à 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 une chaîne checked attend « true »/« false ». Donc ma question est, comment puis-je obtenir cette liaison bidirectionnelle w/cette interface utilisateur et le modèle?

+10

Pour les versions Knockout> = 3.0, voir [la réponse de Natan] (http://stackoverflow.com/a/20764607/606662) pour une solution plus simple que celle suggérée par la réponse acceptée. 07 févr.. 142014-02-07 20:37:31

74

Une option est d'utiliser un writeable computed observable.

Dans ce cas, je pense qu'une bonne option est de rendre l'observable calculé accessible en écriture "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 lierait 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/

  0

Cela semble être une excellente solution. J'utilise le mapping pluggin pour rafraîchir ma machine virtuelle. Savez-vous que cela effacerait ForEditing sur IsMale? 12 avril. 122012-04-12 16:32:29

  0

Si vous utilisez le plugin de mapping, alors vous devrez utiliser les callbacks 'create' (http://knockoutjs.com/documentation/plugins-mapping.html#customizing_object_construction_using_create) dans les mappages ou ajouter l'observable ForEditing après votre les données ont été initialisées (et après l'ajout de nouvelles données). 12 avril. 122012-04-12 17:17:41

+25

Voici une alternative qui pousse la création observable calculée dans une liaison personnalisée, ce qui fait que vous n'avez pas à vous soucier de la traiter dans le plugin de mapping: http://jsfiddle.net/rniemeyer/utsvJ/ 12 avril. 122012-04-12 17:25:13

  0

Merci beaucoup! 12 avril. 122012-04-12 17:32:24

  0

JS mis à jour, parce que la ressource gérée sur les deux jsfiddles ci-dessus sont 404'ing: http://jsfiddle.net/utsvJ/12/ 19 oct.. 122012-10-19 12:30:52

  0

mis à jour les fiddles. Merci! 19 oct.. 122012-10-19 13:33:50

+1

+1 pour utiliser une reliure au lieu de créer une observable sur chaque objet 29 mars. 132013-03-29 03:49:07

  0

@ La solution de RPNiemeyer fonctionne génial pour moi en utilisant le plugin de cartographie 17 mai. 132013-05-17 17:00:10

  0

@RPNiemeyer est certainement la voie à suivre. 31 juil.. 132013-07-31 21:15:10

  0

Voici une autre version qui utilise des extendeurs à la place des bindings: http://jsfiddle.net/siimv/mQ7CU/ Peut s'avérer utile si vous avez différentes liaisons où vous devez utiliser la valeur de chaîne de boolean. 14 août. 132013-08-14 18:51:17

  0

Cette solution fonctionne définitivement, mais il semble que ce soit un tel cerceau à franchir! Est-ce que la liaison à true/false avec des boutons radio est peu commune et ne fait pas partie de la fonctionnalité principale de knockout? 10 déc.. 132013-12-10 14:18:06

  0

@RPNiemeyer merci! ce violon dans les commentaires est celui qui a fonctionné pour moi. 17 déc.. 152015-12-17 15:55:21


10

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> 
  0

le seul problème que je vois est que lors de la sérialisation de viewmodel pour passer au serveur, vous obtiendrez des entiers dans observable (au lieu de booléens). Vous devrez appeler 'vm.IsMale (!! vm. + IsMale())' avant de sérialiser json pour envoyer au serveur (au cas où le côté serveur ne peut pas le gérer correctement) 12 avril. 122012-04-12 16:10:25

  0

Je pense que vous avez raison, mais ma connaissance Javascript est manquant. Pouvez-vous s'il vous plaît m'expliquer comment !! + fonctionne? Je ne suis pas familier avec cette syntaxe. 12 avril. 122012-04-12 16:40:22

  0

@ C.J. ceci convertit la chaîne ou le nombre en booléen - chek ceci http://jsfiddle.net/nickolsky/6ydLZ/, si la chaîne est déjà booléenne elle la gardera comme booléenne 12 avril. 122012-04-12 16:46:24

  0

Merci beaucoup. Je pense que c'est une excellente solution aussi. 12 avril. 122012-04-12 17:31:33

+1

Cependant, cela ne fonctionne pas dans les deux sens. Les boutons radio ne sont pas vérifiés lorsqu'ils sont chargés. 02 déc.. 132013-12-02 13:27:23

  0

ça marche, c'était juste du vieux violon - réparé. À un certain point, github n'autorise pas la liaison de fichiers bruts de jsfiddle, de sorte que certains vieux devinettes ne fonctionnent plus. 02 déc.. 132013-12-02 17:09:03


0

Pourquoi ne pas tout 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> 
+2

on pourrait aussi demander pourquoi ne pas simplement utiliser 1 et 0 au lieu de vrai et faux ... 29 avril. 132013-04-29 14:05:20

+2

si vous envoyez des objets json cela fait une différence. 17 mai. 132013-05-17 18:31:52


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); 
     } 
    } 
}; 

Utiliser ce liant au lieu de créer observables stupides ko calculées.

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> 

112

Je sais que c'est un vieux fil, mais je le même problème et a trouvé une bien meilleure solution qui a été probablement ajouté à KO après cette question a été officiellement répondu, je Je vais laisser ça aux gens qui ont le même problème.

Actuellement, il n'y a pas besoin d'extensions, de gestionnaires de liaison personnalisés ou de calculs. Il suffit de fournir une option "checkedValue", elle l'utilisera à la place de l'attribut "value" html, 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"/> 
  0

Il ressemble à la meilleure solution, ne fonctionne pas pour moi, étrange 22 janv.. 142014-01-22 03:39:03

+2

En regardant la source, il semble prendre la décision d'utiliser la valeur vérifiée [ici] (https://github.com/knockout/knockout/blob/master /src/binding/defaultBindings/checked.js#L16). La valeur 'useCheckedValue' est définie sur true [si l'entrée est une radio ou une case à cocher avec la valeur sous la forme d'un tableau] (https://github.com/knockout/knockout/blob/master/src/binding/defaultBindings/checked. js # L78). En outre, j'utilise Knockout 3.0. Voyez si cela aide. 22 janv.. 142014-01-22 12:50:00

+1

ouais, merci, j'ai mis à jour 3.0.0 et maintenant c'est là. Encore besoin d'envelopper mes valeurs booléennes dans une chaîne parce que le code du serveur attendait ces ;-) 22 janv.. 142014-01-22 21:15:02

  0

Bonne info. Deux points supplémentaires: (1) J'ai trouvé qu'avec Knockout pré-v3.0 je pouvais utiliser la liaison 'value' puis la liaison' checked' (dans cet ordre), mais avec 3.0 je devais utiliser la valeur 'checkedValue 'liant au lieu de la liaison' value'. (2) pré-v3.0 était pointilleux sur le fait que la liaison 'value' précédait la liaison' checked' pour fonctionner correctement, donc j'ai le sentiment que les choses pourraient aussi mieux fonctionner dans v3.0 dans tous les scénarios si vous mettez le bind 'checkedValue' avant la liaison' checked', comme ils le montrent dans [docs] (http://knockoutjs.com/documentation/checked-binding.html). 13 févr.. 142014-02-13 00:35:12

  0

@ ZiglioNZ une façon dont cela peut échouer est si votre setter 'checked' (ou peut-être quelque chose qui en dépend) soulève une erreur - alors la case correcte n'est pas vérifiée 04 août. 142014-08-04 00:39:42

  0

J'utilise la version 3.4 et trouve que je dois encore mettre value = "true" et ce qui précède pour le faire fonctionner. 15 juin. 162016-06-15 17:16:52

  0

Quelqu'un, s'il vous plaît marquer cela comme "la réponse" - c'est très propre. 01 sept.. 172017-09-01 14:15:15

  0

Un problème avec ceci est qu'il laisse le bouton radio avec le "faux" décoché. Je cherchais vraiment à basculer entre les deux boutons et visuellement le tenir vérifié. J'avais besoin d'ajouter une méthode pour réinitialiser les boutons radio cochés. 20 sept.. 172017-09-20 20:00:28

  0

Ignorer le commentaire précédent. Doit avoir eu un problème de mise en cache. Ça fonctionne super bien! 20 sept.. 172017-09-20 20:21:39


0

Vous pouvez également utiliser un extender il est donc facile de les réutiliser pour plus 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; 
}; 

utiliser ensuite comme ceci:

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

La pa Le ramet fourni à boolForEditing indique si la valeur peut être nulle.

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


1

Une fois que vous figurez que le match initial pour le bouton radio veut correspondre uniquement une chaîne et veut mettre la valeur à une chaîne, il est tout simplement une question de la conversion de votre initiale valeur à la chaîne. Je devais me battre avec des valeurs 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 le modèle entier en une seule commande. Ensuite, Razor insère la valeur entre les guillemets pour la conversion. J'utilise un "Vider tout à l'écran" 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 de données, Avant

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

et Après

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

Dans mon projet, je ne l'ai pas besoin de convertir Bools, parce que je Je suis capable d'utiliser une case à cocher qui n'a pas la même frustration.


0

Après avoir fait beaucoup de recherches pour la version plus ancienne de knock-out avant 3.0, il y a peut-être deux meilleures options

Créer une extension à élimination directe 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 de votre modèle, vous « d faire quelque chose comme ce qui 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/