37

I'm trying to find the best way to get an index position within an ngRepeat used with a custom directive. The problem I'm trying to solve is that for each iteration of the ngRepeat, I'd like the option of knowing where I am in the iteration so I can create a custom template based on that index position.

Unless there is a better way of doing this, I'm attempting to do this functionality based on this documentation from directives:

& or &attr - provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given and widget definition of scope: { localFn:'&myAttr' }, then isolate scope property localFn will point to a function wrapper for the count = count + value expression. Often it's desirable to pass data from the isolated scope via an expression and to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).

http://docs.angularjs.org/guide/directive

I'm having some difficulty understanding exactly what this statement is telling me. So, what I have tried to do is this...

First the ngRepeat directive being used along with my directive in "testTemplate":

<ul>
   <li my-attr="test()" my-directive ng-repeat="value in values" class="span2">
   </li>
</ul>

Next, my directive defined:

angular.module('myModule', [])
       .directive('myDirective', function() {
            return {
                replace : true,
                transclude : true,
                scope : {
                    test: '&myAttr'
                },
                templateUrl: 'partials/myTemplate.html',
                link : function(scope, element, attrs) {
                    alert(scope.count);
                }
            }
        });

Now finally, I'm trying to define the "test" function within the controller for the parent template where the directive is being referenced.

function TestTemplateCtrl($scope, testTemplate) {
    $scope.test = function() {
        alert('in test');
        $scope.count = "1";
    }
}

So the first problem is that my "test" function in the parent is not being executed at all. Maybe I'm not understanding how the test function within the parent controller should be called. Now I'm not actually increasing yet either, but is the above the correct way I would go about increasing my count when I do get to that point if I can get the test function to fire?

Community
  • 1
  • 1
DavidB
  • 2,064
  • 3
  • 17
  • 17
  • 2
    Wow I think I was making this more complicated then needed. I can just grab the index within my link function with scope.$parent.$index. I'd still like to know why my "test" function wasn't firing though just for future reference. – DavidB Jan 03 '13 at 05:15

2 Answers2

51

Instead of scope.$parent.$index you might consider passing $index as a directive attribute:

<li my-directive index="{{$index}}" ng-repeat="value in values">

Directive:

myApp.directive('myDirective', function() {
    return {
        replace: true,
        // transclude: true,
        scope: {
            index: '@'
        },
        template: '<div>testing {{index}}</div>',
        link: function(scope, element, attrs) {
            // ...
        }
    }
});

Fiddle.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Good call on this. This is exactly what I was looking to do. – DavidB Jan 04 '13 at 03:53
  • 6
    Of note: with the '@' syntax, you'll have to use attrs.$observe() if you want to use the index value inside the link function -- see [this answer](http://stackoverflow.com/questions/14050195/what-is-the-difference-between-and-in-directive-scope/14063373#14063373) for more info. (You don't need to use $observe() if you are only using the value in a template, like I show in the fiddle.) – Mark Rajcok Jan 04 '13 at 03:58
  • 2
    Personally, I still prefer scope.$parent.$index because adding index attribute is an extra information for users to remember when they try to use your directive. Using scope.$parent.$index in your linker not only allows you to get the same result, but also cleans the directive by reducing this unnecessary attribute. Designing directives in an intuitive way makes it easily reused by other people – zeroliu Sep 22 '14 at 22:13
  • 2
    It's worth noting though, that using the $parent to get to a parent scope can cause hard to find bugs in the future if a new scope is introduced unknowingly in that hierarchy, such as with an ng-if or an ng-repeat. – sma May 18 '15 at 17:30
1

As you pointed out, you can grab the index by using $index.

As why your test function is not firing, you didn't execute it. In your directive's link function you need something like:

scope.$eval(attrs.myAttr);
Bart
  • 19,692
  • 7
  • 68
  • 77
asgoth
  • 35,552
  • 12
  • 89
  • 98