206

I am having a problem binding radio buttons to an object whose properties have boolean values. I am trying to display exam questions retrieved from a $resource.

HTML:

<label data-ng-repeat="choice in question.choices">
  <input type="radio" name="response" data-ng-model="choice.isUserAnswer" value="true" />
  {{choice.text}}
</label>

JS:

$scope.question = {
    questionText: "This is a test question.",
    choices: [{
            id: 1,
            text: "Choice 1",
            isUserAnswer: false
        }, {
            id: 2,
            text: "Choice 2",
            isUserAnswer: true
        }, {
            id: 3,
            text: "Choice 3",
            isUserAnswer: false
        }]
};   

With this example object, the "isUserAnswer: true" property does not cause the radio button to be selected. If I encapsulate the boolean values in quotes, it works.

JS:

$scope.question = {
    questionText: "This is a test question.",
    choices: [{
            id: 1,
            text: "Choice 1",
            isUserAnswer: "false"
        }, {
            id: 2,
            text: "Choice 2",
            isUserAnswer: "true"
        }, {
            id: 3,
            text: "Choice 3",
            isUserAnswer: "false"
        }]
};   

Unfortunately my REST service treats that property as a boolean and it will be difficult to change the JSON serialization to encapsulate those values in quotes. Is there another way to set up the model binding without changing the structure of my model?

Here's the jsFiddle showing non-working and working objects

Jonas
  • 121,568
  • 97
  • 310
  • 388
peteallen
  • 2,553
  • 2
  • 17
  • 16

7 Answers7

392

The correct approach in Angularjs is to use ng-value for non-string values of models.

Modify your code like this:

<label data-ng-repeat="choice in question.choices">
  <input type="radio" name="response" data-ng-model="choice.isUserAnswer" data-ng-value="true" />
  {{choice.text}}
</label>

Ref: Straight from the horse's mouth

kumarharsh
  • 18,961
  • 8
  • 72
  • 100
  • 13
    just wanted to add an important side note: ng-value has to have the value without the curly braces {{}} Example: ng-value="choice2.id" vs value="{{choice2.id}}" – Andi Oct 21 '14 at 11:26
  • Did not know data-ng-model/data-ng-value was a thing. Kept trying to use ng-model and ng-value with no success, but now with the data prefix things are working! Can you explain what exactly the "model-" prefix achieves? I don't see any documentation about it [here](https://docs.angularjs.org/api/ng/directive/ngModel) – Cumulo Nimbus Dec 18 '14 at 17:08
  • 3
    You mean the *data-* prefix I think... The **data-** prefix is to make the HTML valid (although without that also all modern browsers handle the HTML correctly) – kumarharsh Dec 19 '14 at 06:07
  • 8
    I know the post is quite old but I got the same problem now. But when I use your solution and change the radio buttons all that were once selected are true and they don't switch back to false. Is there a solution for this? (problem also exists in OP's Fiddle) – Dominik G Apr 16 '15 at 10:05
  • Can you post a fiddle? – kumarharsh Apr 16 '15 at 13:02
  • Have a look at the Angular Doc: https://docs.angularjs.org/api/ng/input/input%5Bradio%5D – Ilker Cat Aug 13 '15 at 17:19
  • Can anyone explain why I had to the ng-checked attribute to this to get it to work? `` (am using umbraco so forced into angular v1.1.4,perhaps that's it) – jenson-button-event Feb 01 '16 at 20:58
  • I don't think ng-checked has anything to do with radio boxes. Although Angular 1.1.4 is ancient! – kumarharsh Feb 02 '16 at 05:14
21

That's an odd approach with isUserAnswer. Are you really going to send all three choices back to the server where it will loop through each one checking for isUserAnswer == true? If so, you can try this:

http://jsfiddle.net/hgxjv/4/

HTML:

<input type="radio" name="response" value="true" ng-click="setChoiceForQuestion(question1, choice)"/>

JavaScript:

$scope.setChoiceForQuestion = function (q, c) {
    angular.forEach(q.choices, function (c) {
        c.isUserAnswer = false;
    });

    c.isUserAnswer = true;
};

Alternatively, I'd recommend changing your tack:

http://jsfiddle.net/hgxjv/5/

<input type="radio" name="response" value="{{choice.id}}" ng-model="question1.userChoiceId"/>

That way you can just send {{question1.userChoiceId}} back to the server.

Langdon
  • 19,875
  • 18
  • 88
  • 107
  • Thanks for your response. I know your second solution is ideal; however, the app actually supports multiple types of questions, including "check all that apply" questions -- hence the reason for the value being stored on each choice. From what I can tell, your first solution works for updating the model after a selection is made, but not for displaying the model retrieved from the server with a selection already made. – peteallen Jun 06 '13 at 19:46
  • 2
    Ah, true. You can resolve that by using `ngChecked`, except you'll have to break away from using true/false as strings. Can you do that? http://jsfiddle.net/hgxjv/6/ – Langdon Jun 06 '13 at 19:56
  • 3
    Yes, that works! I had tried using ngChecked before, but had not removed the ngModel attribute. It didn't work with that configuration, and I had assumed that it was because ngChecked wasn't compatible with radio buttons. Removing the ngModel attribute and using ngChecked and binding ngClick to your setChoiceForQuestion function achieves what I'm trying to do. Thank you for your help! – peteallen Jun 06 '13 at 21:33
12
 <label class="rate-hit">
     <input type="radio" ng-model="st.result" ng-value="true" ng-checked="st.result">
     Hit
 </label>
 &nbsp;&nbsp;
 <label class="rate-miss">
     <input type="radio" ng-model="st.result" ng-value="false" ng-checked="!st.result">
     Miss
 </label>
Dale K
  • 25,246
  • 15
  • 42
  • 71
Ronel Gonzales
  • 125
  • 1
  • 7
9

I tried changing value="true" to ng-value="true", and it seems to work.

<input type="radio" name="response2" data-ng-model="choice.isUserAnswer" ng-value="true" />

Also, to get both inputs to work in your example, you'd have to give different name to each input -- e.g. response should become response1 and response2.

Bob Gilmore
  • 12,608
  • 13
  • 46
  • 53
seung
  • 426
  • 5
  • 7
1

You might take a look at this:

https://github.com/michaelmoussa/ng-boolean-radio/

This guy wrote a custom directive to get around the issue that "true" and "false" are strings, not booleans.

Karen Zilles
  • 7,633
  • 3
  • 34
  • 33
0

The way your radios are set up in the fiddle - sharing the same model - will cause only the last group to show a checked radio if you decide to quote all of the truthy values. A more solid approach will involve giving the individual groups their own model, and set the value as a unique attribute of the radios, such as the id:

$scope.radioMod = 1;
$scope.radioMod2 = 2;

Here is a representation of the new html:

<label data-ng-repeat="choice2 in question2.choices">
            <input type="radio" name="response2" data-ng-model="radioMod2" value="{{choice2.id}}"/>
                {{choice2.text}}
        </label>

And a fiddle.

rGil
  • 3,719
  • 1
  • 22
  • 30
0

if you are using boolean variable to bind the radio button. please refer below sample code

<div ng-repeat="book in books"> 
<input type="radio" ng-checked="book.selected"  
ng-click="function($event)">                        
</div>
F0XS
  • 1,271
  • 3
  • 15
  • 19
Sahitya M
  • 11
  • 4