2

I feel like i'm just missing something here, but I can't see what. I can't seem to get the value of the radio button to behave in the same way as the checkboxes. What am I doing wrong?

Here's a demo: http://jsfiddle.net/vwJ6a/2/

Here's an idea of what my HTML looks like

<tr ng-repeat="contact in ContactsList">
    <td>{{contact.name}}</td>
    <td>{{contact.email}}</td>
    <td ng-class="{'success':contact.isPrimary}">
        <input type="radio" name="radio-primary" ng-model="contact.isPrimary" />
    </td>
    <td ng-class="{'success':contact.isTechnical}">
        <input type="checkbox" ng-model="contact.isTechnical" />
    </td>
</tr>

And here's my Controller

function MyCtrl($scope) {
    $scope.ContactsList = [{
        name: "John Doe",
        email: "joDoe@domain.com",
        isPrimary: false,
        isTechnical: true
    }, {
        name: "Jane Doe",
        email: "jaDoe@domain.com",
        isPrimary: true,
        isTechnical: false
    }, {
        name: "Bill Murray",
        email: "bMurray@domain.com",
        isPrimary: false,
        isTechnical: false
    }, {
        name: "Someone Dude",
        email: "someone@domain.com",
        isPrimary: false,
        isTechnical: false
    }];
}
Chris Barr
  • 29,851
  • 23
  • 95
  • 135
  • I know your pain - I had a similar issue a few weeks back - might be helpful: http://stackoverflow.com/questions/23817649/angular-binding-checkboxes-to-ng-model-from-ng-repeat – tymeJV Jun 19 '14 at 13:02
  • @tymeJV There's no marked answer on that question - did you ever find a solution? – Chris Barr Jun 19 '14 at 13:26
  • 1
    Just what I posted in the edited question - everything else seemed to fail out....had no idea radio's with Angular would be so....unlogical (in my mind anways) – tymeJV Jun 19 '14 at 13:43

3 Answers3

5

Nice question! I was stuck on a similar problem and implemented kind of a work around. First of all, I don't think that you want the radio buttons to behave in the same way like the checkboxes. Checkboxes allow multiple values while radios don't. Probably that was clear, I just want to state it in order to prevent a misunderstanding.

The AngularJS documentation gives some explanation on the usage of radio buttons. https://docs.angularjs.org/api/ng/input/input[radio] The explanation doesn't take a real "dynamic" environment in account. As you can see, the colours are prefixed in the example. I assume, that you want your table to be completely dynamic, without prefixing anything.

The first thing I did are the following changes in the HTML:

<form name="myForm" ng-controller="MyCtrl">
    <h4>Primary: {{GetPrimaryContact().email}}</h4>

    <table class="table table-bordered table-striped">
        <thead>
        <tr>
            <th>Name</th>
            <th>Email</th>
            <th>Primary</th>
            <th>Technical</th>
            <th>Sales</th>
            <th>Billing</th>
            <th>Emergency</th>
        </tr>
        </thead>
        <tbody>
        <tr ng-repeat="contact in ContactsList">
            <td>{{contact.name}}</td>
            <td>{{contact.email}}</td>
            <td ng-class="{'success':contact.isPrimary == 'true'}">
                <input type="radio" name="radio-primary" ng-model="contact.isPrimary" value="true" ng-change="update($index)" />
            </td>
            <td ng-class="{'success':contact.isTechnical}">
                <input type="checkbox" ng-model="contact.isTechnical" />
            </td>
            <td ng-class="{'success':contact.isSales}">
                <input type="checkbox" ng-model="contact.isSales" />
            </td>
            <td ng-class="{'success':contact.isBilling}">
                <input type="checkbox" ng-model="contact.isBilling" />
            </td>
            <td ng-class="{'success':contact.isEmergency}">
                <input type="checkbox" ng-model="contact.isEmergency" />
            </td>
        </tr>
        </tbody>
    </table>
</form>

The success class-check compares isPrimary to string 'true'. I am not able to use real boolean values when working with comparing on radio buttons.

Additionally, there is a value="true" now which is required for the comparison (It checks the line, where isPrimary is in fact true). Last thing is a new method in the ng-change part. We need this, because we need to tell the controller that the other values are false, when one value changes to true. This is why we also give the $index of the ng-repeat iteration to the method, because the value which is currently changed to true doesn't need to be false again.

Here is the new Controller:

function MyCtrl($scope) {
    $scope.ContactsList = [{
        name: "John Doe",
        email: "joDoe@domain.com",
        isPrimary: 'false',
        isTechnical: true,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Jane Doe",
        email: "jaDoe@domain.com",
        isPrimary: 'true',
        isTechnical: false,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Bill Murray",
        email: "bMurray@domain.com",
        isPrimary: 'false',
        isTechnical: false,
        isSales: true,
        isBilling: false,
        isEmergency: false
    }, {
        name: "Someone Dude",
        email: "someone@domain.com",
        isPrimary: 'false',
        isTechnical: false,
        isSales: false,
        isBilling: false,
        isEmergency: true
    }];

    $scope.GetPrimaryContact = function () {
        return _.findWhere($scope.ContactsList, {
            isPrimary: 'true'
        });
    };

    $scope.update = function(index) {
        for (var i=0;i<$scope.ContactsList.length;i++) {
            if (index != i) {
                $scope.ContactsList[i].isPrimary = 'false';
            }
        }
    };
}

The controller includes the changes, that isPrimary uses Strings instead of Booleans and the new update() method. Here is a link to a working demo in jsfiddle: http://jsfiddle.net/vwJ6a/20/

Update:

Chris and me found obviously a way, to use real boolean values for the radio button in ng-repeat. In order to use it, ng-value="true" has to be used instead of value="true". value does not seem to work, I got the idea here: AngularJS - Binding radio buttons to models with boolean values

The HTML part will look like this:

<form name="myForm" ng-controller="MyCtrl">
    <h4>Primary: {{GetPrimaryContact().email}}</h4>

    <table class="table table-bordered table-striped">
        <thead>
        <tr>
            <th>Name</th>
            <th>Email</th>
            <th>Primary</th>
            <th>Technical</th>
            <th>Sales</th>
            <th>Billing</th>
            <th>Emergency</th>
        </tr>
        </thead>
        <tbody>
        <tr ng-repeat="contact in ContactsList">
            <td>{{contact.name}}</td>
            <td>{{contact.email}}</td>
            <td ng-class="{'success':contact.isPrimary}">
                <input type="radio" name="radio-primary" ng-value="true" ng-model="contact.isPrimary" ng-checked="contact.isPrimary" ng-change="UpdatePrimary(contact)" />
            </td>
            <td ng-class="{'success':contact.isTechnical}">
                <input type="checkbox" ng-model="contact.isTechnical" />
            </td>
            <td ng-class="{'success':contact.isSales}">
                <input type="checkbox" ng-model="contact.isSales" />
            </td>
            <td ng-class="{'success':contact.isBilling}">
                <input type="checkbox" ng-model="contact.isBilling" />
            </td>
            <td ng-class="{'success':contact.isEmergency}">
                <input type="checkbox" ng-model="contact.isEmergency" />
            </td>
        </tr>
        </tbody>
    </table>
</form>

and the controller like this:

function MyCtrl($scope) {
    $scope.ContactsList = [{
        name: "John Doe",
        email: "joDoe@domain.com",
        isPrimary: false,
        isTechnical: true,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Jane Doe",
        email: "jaDoe@domain.com",
        isPrimary: true,
        isTechnical: false,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Bill Murray",
        email: "bMurray@domain.com",
        isPrimary: false,
        isTechnical: false,
        isSales: true,
        isBilling: false,
        isEmergency: false
    }, {
        name: "Someone Dude",
        email: "someone@domain.com",
        isPrimary: false,
        isTechnical: false,
        isSales: false,
        isBilling: false,
        isEmergency: true
    }];

    $scope.GetPrimaryContact = function () {
        return _.findWhere($scope.ContactsList, {
            isPrimary: true
        });
    };

    $scope.UpdatePrimary = function(contact) {
        _.each($scope.ContactsList, function(x) {
            x.isPrimary = (x.email === contact.email);
        });
    };
}

Here is a demo in jsfiddle: http://jsfiddle.net/vwJ6a/24/

Community
  • 1
  • 1
meberhard
  • 1,797
  • 1
  • 19
  • 24
  • 1
    Excellent! This puts me on the right path for sure, but man that just makes me sad that this is the way Angular makes us do this simple task. I'd really rather not use string boolean values if at all possible. I've got this mostly working based on your example, but it's not initially checking the radio button: http://jsfiddle.net/vwJ6a/21/ – Chris Barr Jun 19 '14 at 14:17
  • You can get it easily to work with just exchanging `value` with `ng-value`. http://jsfiddle.net/vwJ6a/24/ Furthermore: I think that AngularJS is a really good framework and most of the things work, as I expected them too! Also the `ng-model` approach works in your situation. I think it's natural to update the other values manually. Could you please also accept the answer if it fixed your problem :) – meberhard Jun 19 '14 at 14:23
  • 1
    Actually I ended up needing this solution. Could you please update it to reflect the changes you made on jsfiddle in the most recent comment? Thanks! – Chris Barr Jun 19 '14 at 18:18
  • 1
    Sure, I updated the answers and described the solution, with which also boolean values are working! – meberhard Jun 20 '14 at 09:37
  • You don't need to send the contact in ng-change"UpdatePrimary(contact)". This is true: $scope.UpdatePrimary = function() { // will know this.contact.whatever } – Robbie Smith Apr 05 '15 at 13:31
  • You're better off using ng-click="UpdatePrimary()". ng-change will only fire the first time you change the value to something new. Going back and forth on a value does not allow the UpdatePrimary() function to fire the 2nd time. – Robbie Smith Apr 05 '15 at 13:34
5

The main difference is checkboxes allow multiples selections while radio buttons dont. That's why radio buttons should share the same model. In your case, the ngModels point to different objects.

This is how I would get around it:

<td ng-class="{'success':primaryContact.email == contact.email}">
    <input type="radio" name="radio-primary" ng-model="primaryContact.email" 
    ng-value="contact.email"/>
</td>

JS

$scope.primaryContact = {
    email:"jaDoe@domain.com"
};

Updated Fiddle

AlwaysALearner
  • 43,759
  • 9
  • 96
  • 78
0

I believe if you add value="1" to your radio input line, you'll get the results you're looking for.

<input type="radio" value="1" name="radio-primary" ng-model="contact.isPrimary" />
Matt M
  • 3,699
  • 5
  • 48
  • 76
  • I'm looking at the fiddle in your link and the Jane Doe radio button is selected, which matches your model. – Matt M Jun 19 '14 at 13:42
  • yes, but try interacting with it. It does not update the model properly or the background color via applied CSS class. Compare that to how the checkboxes work. – Chris Barr Jun 19 '14 at 13:42