Angular radio button model inside ng-repeat

22,735

Solution 1

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: "[email protected]",
        isPrimary: 'false',
        isTechnical: true,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Jane Doe",
        email: "[email protected]",
        isPrimary: 'true',
        isTechnical: false,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Bill Murray",
        email: "[email protected]",
        isPrimary: 'false',
        isTechnical: false,
        isSales: true,
        isBilling: false,
        isEmergency: false
    }, {
        name: "Someone Dude",
        email: "[email protected]",
        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: "[email protected]",
        isPrimary: false,
        isTechnical: true,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Jane Doe",
        email: "[email protected]",
        isPrimary: true,
        isTechnical: false,
        isSales: false,
        isBilling: true,
        isEmergency: true
    }, {
        name: "Bill Murray",
        email: "[email protected]",
        isPrimary: false,
        isTechnical: false,
        isSales: true,
        isBilling: false,
        isEmergency: false
    }, {
        name: "Someone Dude",
        email: "[email protected]",
        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/

Solution 2

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:"[email protected]"
};

Updated Fiddle

Share:
22,735
FiniteLooper
Author by

FiniteLooper

I'm a front-end web designer and developer. I make user interfaces better, write a lot of Angular code, and I've usually got some coffee

Updated on June 20, 2020

Comments

  • FiniteLooper
    FiniteLooper almost 4 years

    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: "[email protected]",
            isPrimary: false,
            isTechnical: true
        }, {
            name: "Jane Doe",
            email: "[email protected]",
            isPrimary: true,
            isTechnical: false
        }, {
            name: "Bill Murray",
            email: "[email protected]",
            isPrimary: false,
            isTechnical: false
        }, {
            name: "Someone Dude",
            email: "[email protected]",
            isPrimary: false,
            isTechnical: false
        }];
    }