Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object equality comparison for input[radio] with ng-model and ng-value

Let me start by saying that this question is very similar to issues with selection in a <select> tag using ng-options. For example, Working with select using AngularJS's ng-options. The specific problem is comparing two different instances of an object which are not reference equal, but which logically represent the same data.

To demonstrate, let's say we have the following array of options and selected option variable in the model:

$scope.items = [
   {ID: 1, Label: 'Foo', Extra: 17},
   {ID: 2, Label: 'Bar', Extra: 18},
   {ID: 3, Label: 'Baz', Extra: 19}
];
$scope.selectedItem = {ID: 1, Label: 'Foo'};

Note that the above objects are just for demonstration. I specifically left off the 'Extra' property on selectedItem to show that sometimes my model objects differ in their specific properties. The important thing is that I want to compare on the ID property. I have an equals() function on my real objects that compares both prototype 'class' and ID.

And then in the view:

<label class="radio inline" ng-repeat="item in items">
    <input type="radio" ng-model="selectedItem" ng-value="item"> {{item.Label}}
</label>

Now, the problem here is that the radio button for 'Foo' will not start selected, because angular is using reference equality for the objects. If I changed the last line in my scope to the below, everything would work as expected.

$scope.selectedItem = items[0];

But, the problem I'm having is that in my application, I'm not simply declaring these two simple variables in scope. Rather, the options list and the data structure where the selected option are being bound are both part of larger sets of JSON data that are queried from the server using $http. In the general case, it's very difficult for me to go change the data-bound selected property to be the equivalent option from my data query.

So, my question: In ng-options for the <select>, angular offers a track by expression that allows me to say something like "object.ID" and inform angular that it should compare the selected model value to the options via the ID property. Is there something similar that I can use for a bunch of radio inputs all bound to the same model property? Ideally, I would be able to tell angular to use my own custom equals() method that I've placed on these model objects, which checks both object type as well as ID. Failing that though, being able to specify ID comparison would also work.

like image 864
Dana Cartwright Avatar asked Oct 09 '13 19:10

Dana Cartwright


People also ask

What is the difference between ng-model and data NG model?

The answer is: (ngModel) causes a 1-way data-binding, whereas [(ngModel)] ensures a two-way data binding.

What is the difference between ngValue and ngModel?

Also ngValue is a one-way binding, and ngModel is a two-way binding.

What is NG-model and Ng change?

Ng-change is a directive in AngularJS which is meant for performing operations when a component value or event is changed. In other words, ng-change directive tells AngularJS what to do when the value of an HTML element changes. An ng-model directive is required by the ng-change directive.

What is NG-model used for?

The ngModel directive is a directive that is used to bind the values of the HTML controls (input, select, and textarea) or any custom form controls, and stores the required user value in a variable and we can use that variable whenever we require that value. It also is used during form validations.


1 Answers

I write a most simple directive. Using a kind of "track-by" to map two different objects. See the http://jsfiddle.net/xWWwT/146/.

HTML

<div ng-app="app">
<div ng-app ng-controller="ThingControl">    
    <ul >
        <li ng-repeat="color in colors">
            <input type="radio" name="color" ng-model="$parent.thing" ng-value="color" radio-track-by="name" />{{ color.name }}
        </li>
    </ul>
    Preview: {{ thing }}
</div>
</div>

JS

var app = angular.module('app', []);

app.controller('ThingControl', function($scope){
    $scope.colors = [
        { name: "White", hex: "#ffffff"},
        { name: "Black", hex: "#000000"},
        { name: "Red", hex: "#000000"},
        { name: "Green", hex: "#000000"}
    ];

    $scope.thing = { name: "White", hex: "#ffffff"};

});

app.directive('radioTrackBy', function(){
return {
        restrict: "A",
        scope: {
            ngModel: "=",
            ngValue: "=",
            radioTrackBy: "@"
        },
        link: function (ng) {   
            if (ng.ngValue[ng.radioTrackBy] === ng.ngModel[ng.radioTrackBy]) {                                
                ng.ngModel = ng.ngValue;
            }
        }
    };
});
like image 111
Rodolfo Jorge Nemer Nogueira Avatar answered Sep 18 '22 12:09

Rodolfo Jorge Nemer Nogueira