How to bind to complex objects with radios and checkboxes in AngularJS?
Asked Answered
B

2

11

Say we have a set of projects exposed via the Project service:

{ id: '123', name: 'Yeoman', watchers: '1233', ... }
{ id: '123', name: 'Grunt', watchers: '4343', ... }

Then, we have a form to choose your favorite project:

Select favorite project:
%label.radio(ng-repeat="project in Project.query()")
  %input(type="radio" ng-model="data.favoriteProject" value="{{project.id}}") {{project.name}}

This sets choices.favoriteProject to the id value of the chosen project. Often, we need to access the related object, not just the id:

John's favorite project:
{{Project.get(data.favoriteProject).name}}

What I'm looking for is a way to bind the radios and checkboxes straight to the object itself, not the id, so we could do

John's favorite project:
{{data.favoriteProject.name}}

instead. This is possible with select directive via ng-options, but how can we do this with radios and checkboxes? I'd still like to use the ids for matching instead of references, if possible.

To clarify, here's an example what I'm looking for

Select favorite project:
%label.radio(ng-repeat="project in Project.query()")
  %input(type="radio" ng-model="data.favoriteProject" value="{{project}}" ng-match="id") {{project.name}}

It says: "Please bind data.favoriteProject to the actual project object and use the id to check if they match (instead of references)".

Burstone answered 15/1, 2013 at 13:8 Comment(0)
T
15

[Update]

I've completely changed my answer after discovering the ngValue directive, which appears to be undocumented. It allows you to bind objects instead of just strings as values for ngModel on the radio button inputs.

<label ng-repeat="project in projects">
  <input type="radio" ng-model="data.favoriteProject"
    ng-value="project">{{project.name}}</input>
</label>

<p>Your favorite project is {{data.favoriteProject.name}}.</p>

This uses references to check rather than just IDs, but I think in most cases, this is what people will be looking for. If you do very strictly only want to match based on IDs, you can use the [Old Answer], below, or even better, just create a function--e.g. projectById(projectId) that you can use for looking up a project based on its ID.

I've updated the JSFiddle to demonstrate: http://jsfiddle.net/BinaryMuse/pj2GR/


[Old Answer]

Perhaps you can utilize the ng-change attribute of the radio button directive to achieve what you want. Consider this HTML:

<p>Select your favorite project:</p>
<label ng-repeat="project in projects">
  <input type="radio" ng-model="data.favoriteProjectId" value="{{project.id}}"
         ng-change="data.favoriteProject = project">
    {{project.name}}
  </input>
</label>

<p>Your favorite project is {{data.favoriteProject.name}}.</p>

You could also call a function inside ng-change, for example setfavoriteProject(project)--but I did not do that here for simplicity's sake.

Here is a working jsFiddle to demonstrate the technique: http://jsfiddle.net/BinaryMuse/pj2GR/7/

Totten answered 16/1, 2013 at 6:20 Comment(8)
Thank you! I really like this solution. It's simple, yet it works neatly! I'll leave the question open for other solutions for now. The problem with this is that there's duplication of data (the id) and we have to manage setting the value ourselves.Burstone
Why use projects[$index] instead of just the project object?Burstone
I made that refactoring in the jsFiddle, but forgot to update the post. Thanks!Totten
There's a slight problem with checkboxes with this approach. ng-change fires on check AND uncheck so the favoriteProject gets set even if we are unchecking.Burstone
Interesting that the ng-model is more or less a throw-away here.Clasping
@Clasping Yes, to a point. Your comment caused me to investigate further, and I found a much better solution in the form of ngValue; I've updated my answer accordingly.Totten
@BrandonTilley Fascinating. I'll try to find out why it's not documented.Clasping
ng-value is the way to go, excelent!Husted
R
1

No ng-change needed (and I'm not sure, if it is a good practise to write inline-code like this. On the other hand angulars directives are not too far from it theirselves). Why not just do something like this (works with ng-repeat as well):

Fiddle: http://jsfiddle.net/JohannesJo/VeZxh/3/

Code:

<body ng-app="app">
<div ng-controller='controller'>
<input type = "radio" ng-model = "oSelected"  value = "{{aData[0]}}">
<input type = "radio" ng-model = "oSelected"  value = "{{aData[1]}}">
<div>test:     {{oSelected}}</div>
</div>
</body>


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

app.controller('controller', function($scope){
$scope.aData = [
    {o:1},
    {o:2}
];
$scope.oSelected = {};
});

Edit: I maybe should mention that this doesn't work for checkboxes, as value will either be true or false. Also a shared model will lead to all checkboxes either being checked or unchecked at the same time.

Rescue answered 28/2, 2013 at 21:8 Comment(1)
see ng-value solution, it is pretty much flexibleHusted

© 2022 - 2024 — McMap. All rights reserved.