As Ven mentioned, ng-repeat
does create a child scope for each item in the loop. The child scopes do have access to the parent scope's variables and methods through prototypal inheritance. The confusing part is when you make an assignment, it adds a new variable to the child scope rather than updating the property on the parent scope. In ng-click
, when you make an assignment call tiggerTitle =e.name
, it actually adds a new variable called triggerTitle
to the child scope. The AngularJS docs explains this well in the section here called JavaScript Prototypal Inheritance.
So how do you get around this and set the model variable properly?
A quick and dirty solution is to access the parent scope using $parent
like so.
<a ng:click="$parent.triggerTitle=e.name; $parent.triggerEvent = e.action;">...
Click to see a working version of your Fiddle using the $parent solution.
The use of $parent
can cause issues if you are dealing with nested templates or nested ng-repeats. A better solution may be to add a function to the controller's scope which returns a reference to the controller's scope. As already mentioned, the child scopes have access to call the parent functions, and thus can reference the controller's scope.
function MyCtrl($scope) {
$scope.getMyCtrlScope = function() {
return $scope;
}
...
<a ng-click="getMyCtrlScope().triggerTitle=e.name;getMyCtrlScope().triggerEvent = ...
Click to see a working version of your Fiddle using the better method
This Works
<body ng-app="demo" ng-controller="MainCtrl">
<ul>
<li ng-repeat="action in actions" ng-click="action.action()">{{action.name}}</li>
</ul>
<script>
angular.module('demo', ['ngRoute']);
var demo = angular.module('demo').controller('MainCtrl', function ($scope) {
$scope.actions = [
{ action: function () {
$scope.testabc();
}, name: 'foo'
},
{ action: function () {
$scope.testxyz();
}, name: 'bar'
}
];
$scope.testabc = function(){
alert("ABC");
};
$scope.testxyz = function(){
alert("XYZ");
};
});
</script>
</body>
I surfed the internet for so long looking for an answer to the problem of ng-repeat creating its own scope within it. When you change a variable inside ng-repeat, the views don't update everywhere else in the document.
And finally the solution I found was one word, and no one tells you that.
It's $parent. before the variable name and it will change its value in the global scope.
So
ng-click="entryID=1"
becomes
ng-click="$parent.entryID=1"
Instead of this:
<li ng-repeat="e in events">
<a ng-click="triggerTitle=e.name; triggerEvent = e.action;">{{e.action}} {{e.name}}</a>
</li>
Just do this:
<li ng-repeat="e in events">
<a ng-click="$parent.triggerTitle=e.name; $parent.triggerEvent = e.action;">{{e.action}} {{e.name}}</a>
</li>
ng-repeat
creates a new scope, you can use $parent
to access the parent scope from inside the ng-repeat
block.
Here we can use $parent so that we can access the code outside of the ng-repeat.
Html code
<div ng-controller="MyCtrl">
<a ng-click="triggerTitle='This works!'">test</a>
<h5>Please select trigger event: [{{triggerEvent}}] {{triggerTitle}}</h5>
<br /> <br />
<ul class="dropdown-menu">
<li ng-repeat="e in events">
<a ng-click="$parent.triggerTitle=e.name; $parent.triggerEvent = e.action;">{{e.action}} - {{e.name}}</a>
</li>
</ul>
Angular Js code
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.triggerTitle = 'Select Event';
$scope.triggerEvent = 'x';
$scope.triggerPeriod = 'Select Period';
$scope.events = [{action:'compare', name:'Makes a policy comparison'}, {action:'purchase', name:'Makes a purchase'},{action:'addToCart', name:'Added a product to the cart'}]
}
you can test it here http://jsfiddle.net/xVZEX/96/
use this
<div ng:controller="MyCtrl">
<a ng:click="triggerTitle='This works!'">test</a>
<h5>Please select trigger event: [{{triggerEvent}}] {{triggerTitle}}</h5>
<ul class="dropdown-menu">
<li ng:repeat="e in events">
<a ng:click="triggerTitle=e.name; triggerEvent = e.action;">{{e.action}} - {{e.name}}</a>
</li>
</ul>
</div>
I converted ng-click to ng:click and it started working, I am yet to find the reason, just quickly posted to share.