Get ng-repeat complete in angular directive

2019-05-15 05:00发布

In the code below, how does the angular directive 'myscroll' know what ng-repeat elements are being created?

<myscroll>
     <div ng-repeat="a in arr">{{a}}</div>
</myscroll>

I know that the $last event is not fired to the parent directive, how can I solve this?

myapp.directive("myscroll", function () {
    return{
        restrict: "E",
        transclude: true,
        template: "<span class='left'></span><div class='mask' ng-transclude></div><span class='right'></span>",
        link: function (scope, element, attr) {

            scope.$watch("$last",function(){ console.log("ng-repeat rendered") })

        }
    }
})

2条回答
做个烂人
2楼-- · 2019-05-15 05:33

Just in case someone else stumbles upon this question, I wanted to share my solution. dluz's answer pointed me in the right direction, but I took a more 'elegant' approach.

To give you context, I needed to use this system with ng-repeat to populate the options in a select dropdown. There was an incompatibility with ng-options with the jQuery Chosen plugin (beautiful autocomplete/search dropdown plugin, by the way!), and I needed to initialize the plugin once all the options were finished rendering.

HTML:

<select auto-complete ng-model="stuff">
    <option ng-repeat="item in items" value="{{ item.value }}">{{ item.name }}</option>
</select>

JavaScript:

// Extension of built-in ngRepeat directive - broadcasts to its parent when it is finished.
// Passes the last element rendered as an event parameter.
app.directive('ngRepeat', function () {
    return {
        restrict: 'A',
        link: function ($scope, $elem, $attrs) {
            if ($scope.$last)
                $scope.$parent.$broadcast('event:repeat-done', $elem);
        }
    };
});

// My directive for working with the Chosen plugin.
app.directive('autoComplete', function () {
    return {
        restrict: 'A',
        link: function ($scope, $elem, $attrs) {
            $scope.$on('event:repeat-done', function () {
                setTimeout(function () {
                    if (!$elem.children(':first-child').is('[disabled]'))
                        $elem.prepend('<option disabled="disabled"></option>');
                    $elem.chosen({ disable_search_threshold: 10, width: '100%' });
                    $elem.trigger('chosen:updated');
                });
            });
        }
    };
});

As you can see, I simply extended the built-in ng-repeat directive with my broadcaster function. Since it's a non-invasive addition, I have no qualms with implementing this into all uses of ng-repeat.

Within the function itself, instead of looking at $elem.parent().scope() (which could - and in most situations would - work), I use Angular's $scope.$parent to fetch the parent context.

Then I broadcast a custom event (event:repeat-done), and pass the last rendered repeat element as a parameter to the parent scope.

I could then pick up this event in my auto-complete directive and initialize my Chosen plugin over the fully-rendered select element!

This technique could then be generically applied to any time you need to detect when an ng-repeat directive is done. For your example:

myapp.directive("myscroll", function () {
    return{
        restrict: "E",
        transclude: true,
        template: "<span class='left'></span><div class='mask' ng-transclude></div><span class='right'></span>",
        link: function (scope, element, attr) {
            scope.$on("event:repeat-done",function(){ console.log("ng-repeat rendered") })
        }
    }
})

EDIT: For some cases it may be beneficial to detect these events all the way up at the rootScope. If this is the case, you could replace $scope.$parent.$broadcast with $scope.$emit to have the event bubble up instead of down.

查看更多
smile是对你的礼貌
3楼-- · 2019-05-15 05:49

A simple way to do it is to create a new directive called 'repeat-done' and use it where the 'ng-repeat' is. Then you can notify the 'myscroll' directive (parent scope) whatever you need.

<myscroll>
     <div ng-repeat="a in arr" repeat-done>{{a}}</div>
</myscroll>

myapp.directive('repeatDone', [function () {
  return {
    restrict: 'A',
     link: function (scope, element, iAttrs) {

          var parentScope = element.parent().scope();

          if (scope.$last){
               // ng-repeat is completed and you now have access to 'myscroll' scope
               console.log(parentScope);   
          }
        }
      };
    }])
查看更多
登录 后发表回答