I have created an AngularJS directive with radio buttons to control which environment my page will query against and added it to my page. I am using a two-way binding to map a local scope variable called environment
to an app variable with the same name. It seems to work well when I create the radio buttons explicitly in the template (in my actual code I'm using templateUrl
instead, but still have the same problem).
<div>
<label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(1)" value="1" />Testing</label>
<label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(2)" value="2" />Production</label>
</div>
I can select each choice as many times as I want, and the value bubbles all the way up to the app's scope variable.
I wanted to change the creation of the radio buttons to use ng-repeat
on an array of choice objects. It creates the options, and it captures the ngChange
event, but only once for each choice.
<label ng-repeat="choice in choices">
<input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(choice)" value="{{ choice.id }}" />{{ choice.name }}
</label>
Here is the working version fiddle and the relevant parts of my directive code:
template: '<div>'
+ '<label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(1)" value="1" />Testing</label>'
+ '<label><input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(2)" value="2" />Production</label>'
+ '<div>Directive environment: {{ environment }}</div>'
+ '</div>',
link: function(scope, element, attributes) {
scope.changeEnvironment = function(choiceID) {
console.log('SELECTED environment ' + choiceID);
scope.environment = choiceID;
console.log('directive environment = ' + scope.environment);
};
}
And here is the version that only works once:
template: '<div><label ng-repeat="choice in choices">'
+ '<input type="radio" name="env" ng-model="environment" ng-change="changeEnvironment(choice)" value="{{ choice.id }}" />{{ choice.name }}'
+ '</label>'
+ '<div>Directive environment: {{ environment }}</div>'
+ '</div>',
link: function(scope, element, attributes) {
scope.choices = [
{ id: 1, name: "Testing" },
{ id: 2, name: "Production" }
];
scope.changeEnvironment = function(choice) {
console.log('SELECTED environment ' + choice.id);
scope.environment = choice.id;
console.log('directive environment = ' + scope.environment);
};
}
I'm brand-new to AngularJS, so it's entirely possible I'm making a very basic mistake. Can anyone point me in the right direction?
UPDATE As per callmekatootie's suggestion, I changed the event in question from ng-change
to ng-click
, and it fires every time. That will do as a work-around for now, but I originally used ng-change
because I didn't think ng-click
would apply to changes caused by clicking on the text label rather than the input itself, but in fact it does. Still don't get why ng-change
only fires once, though.
The cause of the problem is that ngRepeat
will create new scopes for its children (along with how prototypal inheritance affects scopes).
The Angular wiki has a fairly good (and thorough) explanation of how scope inheritance works (and the common pitfalls).
This answer to a very similar (in nature) problem pretty much explains the issue.
In your specific case, you could introduse an object (e.g. local
) and assign environment
as its property):
scope.local = {environment: scope.environment};
scope.changeEnvironment = function(choice) {
scope.environment = choice.id;
};
Then you should bind your template to that "encaplulated" property:
<input ... ng-model="local.environement" ... />
See, also, this short demo.
UPDATE
This answer intends to point out the root of the issue.
A different implementation (like the ones proposed by tasseKATT or Leon) is definitely recommended (and way more "Angularish").
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With