47

This should be an extremely simple question, but all of the workarounds I've found are complex. I'm looping through an array of objects in using ng-repeat in a template as follows:

<div class="row-fluid" ng-repeat="message in messages.current|filter:'draft'"> 
    {{ message.subject }} ... {{ campaign.name }} ...
</div>

Since the ng-repeat creates a new scope, the 'campaign' object from the controller doesn't seem to be accessable. Is there any way (aside from adding the campaign object to every item in my array) of getting that value?

Thanks in advance.

Wandering Digital
  • 1,788
  • 2
  • 21
  • 27
  • 3
    It should work. is {{ campaign.name }} working outside ng-repeat? – Anoop May 15 '13 at 20:04
  • 8
    The child scopes that ng-repeat creates prototypically inherit from the parent scope, so all properties defined on the parent scope should be visible in the ng-repeat child scopes. Do you have another directive defined on the div or inside it? – Mark Rajcok May 15 '13 at 20:04
  • Okay. I got it. Thanks to you both for the comments. – Wandering Digital May 15 '13 at 20:51

3 Answers3

77

You can access the parent scope by using $parent

<div class="row-fluid" ng-repeat="message in messages.current|filter:'draft'"> 
    {{ message.subject }} ... {{ $parent.campaign.name }} ...
</div>
Mathew Berg
  • 28,625
  • 11
  • 69
  • 90
  • 21
    Though this is true, if you are doing it that way, you are doing some things wrong. As child scopes prototypally inherit from parent scope, you do not need to this. It would be messy when the code grows and you will find yourself writing `$parent.$parent.$parent.$parent.help()` – Umur Kontacı May 16 '13 at 05:49
  • 4
    While going overboard on $parent is something you should for sure try to stay away from, in some instances it would be useful. Accessing the index of a parent that is also in an ng-repeat is at least one I can think of.
    {{ $parent.$index }} {{ $index }}
    – Mathew Berg May 16 '13 at 09:52
  • 4
    Of course there are times practise beats theory =). I'm sure there can be more examples related, but I feel responsible warning people when I see an anti-pattern, if they are going to use it, they should know what else could happen. – Umur Kontacı May 16 '13 at 10:21
  • 1
    [Understanding-Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) talks about using $parent vs. prototypal inheritance. – cod3monk3y Mar 01 '14 at 18:17
  • @MathewBerg, in that instance you could use `ng-init="parentIndex = $index"` in the ng-repeat to get access to it in the child scope without using `$parent` – adam0101 Jun 22 '16 at 22:17
  • @UmurKontacı I read about "child scopes prototypally inherit from parent scope", question is how to retrieve such inherited properties in the child scope? If $parent.$parent....$parent.help() is not the way to go, what is? Common sense says if you want to access an inherited property in the child class you just do it directly, but in angularjs scope, I cant really call help() in ng-repeat block. Need your help please. – shawhu Jul 01 '16 at 03:02
3

This is a way that works that doesn't use $parent. It searches upwards through the nested scopes to find the object you're using, however many scopes it has to go through.

In the scope that contains the list, you can define an object with the list as a property, like this:

$scope.obj = {};
$scope.obj.items = ['item1','item2','item3'];

Then have the ng-repeat look like this:

<div ng-repeat="item in obj.items | filter:'item3' track by $index">
    {{obj.items[ obj.items.indexOf(item) ]}}
</div>

(you need to use obj.items[ obj.items.indexOf(item) ] rather than obj.items[ $index ] because $index is the index of the filtered array, not the original)

The reason this works is because while obj doesn't exist in the current scope, as you attempt to access its property, Angular will look above the current scope rather than give you an error (if you just tried {{obj}} it would be undefined, and Angular would be happy with giving you nothing instead of looking through higher scopes). This is a helpful link about nested scopes: http://www.angularjshub.com/examples/basics/nestedcontrollers/

In my case I needed the track by $index, because I had an input with ng-model bound to an item in the array, and whenever the model updated, the input would blur because I think the HTML was being re-rendered. A consequence of using track by $index is that items in the array with identical values will be repeated. If you modify one of those other than the 1st one, weird things will happen. Maybe you can filter for uniqueness to avoid that.

I'm relatively new to AngularJS, so please comment if there is anything big I'm missing. But this works, so I'm using it at least.

Michael Pearson
  • 584
  • 1
  • 4
  • 10
  • 1
    "whenever you have ng-model, there's got to be a dot in there somewhere" - [Misko Hevery](http://www.hirez.io/c/angular-2-preparation/e/episode-3-controller-as) at 3:34 – Rocco Apr 25 '16 at 20:08
3

Another method might be to pass parent scope as a scope variable to the directive i.e.

<my-directive 
    md-parent-scope="this" 
    ng-repeat="item in items"></my-directive>

It's a bit messy, but you have more control over what the parent actually is and can pass anything in.

marksyzm
  • 5,281
  • 2
  • 29
  • 27