Ng-click doesn't work inside ng-repeat

44,720

Solution 1

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

Solution 2

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

Solution 3

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.

Solution 4

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/

Solution 5

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"

Share:
44,720

Related videos on Youtube

Alexandru R
Author by

Alexandru R

x

Updated on July 09, 2022

Comments

  • Alexandru R
    Alexandru R almost 2 years

    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>
    
  • Sarah Vessels
    Sarah Vessels almost 11 years
    Downvoted for not solving the problem of ng-click not working inside ng-repeat.
  • Sarah Vessels
    Sarah Vessels over 10 years
    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.
  • Ven
    Ven over 10 years
    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.
  • Ven
    Ven over 10 years
    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.
  • Tony Ennis
    Tony Ennis almost 10 years
    This answer is little more than "RTFM". The excuse that the OP "needs to understand it" doesn't cut it.
  • Ven
    Ven almost 10 years
    Alas, that's how it is. If you're not ready learning parts of the language you use, you're in trouble anyway.
  • Linial
    Linial over 9 years
    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 though
  • Admin
    Admin about 9 years
    A nice case of extra indirection solving all problems. +1
  • Iain Collins
    Iain Collins almost 9 years
    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.
  • Ven
    Ven almost 9 years
    @IainCollins I don't understand what you're saying.
  • Sjon
    Sjon over 8 years
    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.
  • Lyubimov Roman
    Lyubimov Roman over 8 years
    Think the best solution will be "controllerAs"
  • snowYetis
    snowYetis about 8 years
    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.
  • Ven
    Ven about 8 years
    I'm sure you can derive that changing a variable's name everywhere (or a property name) will not change the behavior :-).