Ng-click doesn't work inside ng-repeat
Asked Answered
P

8

27

Ng-click doesn't work from inside ng-repeat. Outside it works. I've put a fiddle here

<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>
Padegs answered 24/5, 2013 at 14:1 Comment(0)
M
52

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

Michell answered 25/9, 2014 at 15:30 Comment(2)
Should be marked as correct answer. But explanation should be simplified into 3 lines total. ng-repeat creates new scope. if you are trying to access $scope from ng-repeat by ng-click, you need to use $parent on that element. thanks for the direction thoughLaski
Think the best solution will be "controllerAs"Transfer
B
12

Because ng-repeat creates a new scope.

This has been answered numerous time, because the nuance is a bit difficult to understand, especially if you don't know everything about js's prototypal inheritance : https://github.com/angular/angular.js/wiki/Understanding-Scopes

EDIT: it seems this answer is very controversial. Just to be clear – this is how JS works. You really shouldn't try to learn Angular before understand how JS works. However, the link does seem to miss

So, here's an example on how JS works in this case:

var a = {value: 5};
var b = Object.create(a); // create an object that has `a` as its prototype

// we can access `value` through JS' the prototype chain
alert(b.value); // prints 5
// however, we can't *change* that value, because assignment is always on the designated object
b.value = 10;
alert(b.value); // this will print 10...
alert(a.value); // ... but this will print 5!

So, how can we work around that?

Well, we can "force" ourselves to go through the inheritance chain – and thus we'll be sure we're always accessing the correct object, whether accessing value or modifying it.

var a = {obj: {value: 5}};
var b = Object.create(a); // create an object that has `a` as its prototype

// we can access `value` through JS' the prototype chain:
alert(b.obj.value); // prints 5
// and if we need to change it,
// we'll just go through the prototype chain again:
b.obj.value = 10;
// and actually refer to the same object!

alert(b.obj.value == a.obj.value); // this will print true
Because answered 24/5, 2013 at 14:3 Comment(11)
Downvoted for not solving the problem of ng-click not working inside ng-repeat.Bozcaada
I understand trying to find working AngularJS examples online has been one of the hardest programming problems I've had. This framework is wonderful but it's an enigma, and its documentation leaves something to be desired.Bozcaada
Asking questions instead of just saying "BAH DOWNVOTED YOUR ANSWER SUCKS" usually is better ;). Now, to stop with the joking, I didn't provide a fixed code because this is something that needs to be understood by the author. This is very important when working with Angular, and if you don't know that rule by heart, you'll spend your time debugging issues arising from not using the dot.Because
Also, I agree that the documentation isn't very descriptive sometimes, but there are a lot of examples around there and a very active community.Because
This answer is little more than "RTFM". The excuse that the OP "needs to understand it" doesn't cut it.Jackdaw
Alas, that's how it is. If you're not ready learning parts of the language you use, you're in trouble anyway.Because
A nice case of extra indirection solving all problems. +1Stockbroker
This doesn't actually provide an answer to a simple and entirely reasonable question and is very misleading. Scope in AngularJS works this way by design but it could just as equally not.Difficulty
@IainCollins I don't understand what you're saying.Because
Little late to the party, but this really should be taken down. You could have combined the JS example of scope with the provided solution to the NG-Click/Repeater problem. That would have been a great post.Hah
I'm sure you can derive that changing a variable's name everywhere (or a property name) will not change the behavior :-).Because
D
6

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.

Difficulty answered 26/8, 2015 at 11:54 Comment(0)
M
3

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/

Militant answered 15/10, 2016 at 11:18 Comment(0)
B
1

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>
Bondholder answered 25/10, 2015 at 16:21 Comment(1)
Please describe your answer, instead of just pasting a block of code. It should help someone understand what (if) they did wrong or were missing.Sufism
L
1

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"

Ledbetter answered 29/5, 2018 at 21:1 Comment(0)
S
0

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.

Spartan answered 27/8, 2013 at 15:42 Comment(0)
A
-1

Use controllers with the 'as' keyword.

Check the documentation on angularjs on controllers.

For the above question:

<div ng-controller="MyCtrl as myCntrl">
 <a ng-click="myCntrl.triggerTitle='This works!'">test</a>
    <h5>Please select trigger event: [{{myCntrl.triggerEvent}}] {{myCntrl.triggerTitle}}</h5>
       <ul class="dropdown-menu">
         <li ng-repeat="e in myCntrl.events">
             <a ng-click="myCntrl.triggerTitle=e.name; myCntrl.triggerEvent = e.action;">{{e.action}} - {{e.name}}</a>
         </li>
       </ul>
</div>

This will attach the properties and functions to the scope of the controller.

Agretha answered 25/3, 2015 at 13:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.