10

So I am looking to move my library of plugins over to Angular wherever possible just to keep things consistent. The problem I am running into is getting directives to run after after any directives on its children have run.

Just to give a little bit of clarity, the goal here is to make it easy for our integrators (CSS/HTML only team members) to add dynamic functionality to items simply by tagging it with a feature. Currently they do this via a data-features attribute. For instance, for an image slider they might tag a UL with a data-features="imageSlider" attribute to make that UL a slider.

Along those lines, I am working on moving that image slider module over to angular. I want my integrators to be able to write something like:

<ul image-slider>
    <li slide>
         My Slide 1
    </li>
    <li slide>
         My Slide 2 
    </li>
    <li slide>
         My Slide 3
    </li>
</ul>

And I can turn that into an image slider dynamically. The above works fine, however if the markup looks like this:

<ul image-slider>
    <li slide ng-repeat="slide in data.slider.slides">
         My Slide {{$index}}
    </li>
</ul>

Then the ng-repeat doesn't finish before the image-slider directive runs, which means I do not have access to all of the slides to work my magic.

Is there a way I can tell the image-slider directive to wait for any directives inside of it to finish before firing?

I have read the following questions already:

None of these seem to have an answer to this problem so I figured I would put together a much more succinct question in the hopes of finding an answer.

Community
  • 1
  • 1
StephenRios
  • 2,192
  • 4
  • 26
  • 42

2 Answers2

8

I suggest a much simpler approach. Use the $timeout function. If you set the $timeout to zero, it will run exactly after everything has ran:

app.directive("imageSlider", [ '$timeout', function($timeout) {
    return function(scope, element, attrs)
    {
        // your data is defined in scope.data.slider.slides

        $timeout(function() 
        {
            // This code will run whenever the page has finished processing
            // So it will run after ng-repeat has finished
        }, 0);
    }
}]);
Kousha
  • 32,871
  • 51
  • 172
  • 296
  • 4
    I would suggest against using timeouts. Timeouts are hacks. They are highly platform dependent, and if a laggy platform (ala kindle or phone) runs it isn't guaranteed to fire after repeat has finished. The solution I gave, while more complex, is guaranteed to run when you expect it. – chubbsondubs Aug 27 '14 at 21:07
  • I ended up ditching this in favor of a custom event I am firing after listening for angular to finish it's initial compile. The idea is the same though, and you are correct, this indeed broke when loading the page slowly etc. after doing some testing. – StephenRios Aug 27 '14 at 21:50
5

So the easiest way to do this is to use directive to directive communication between slide directive and the image-slider directive. Here is what you do:

app.directive("imageSlider", [ '$log', function($log) {
    return {
        scope: {
        },
        controller: function($scope) {

            $scope.slides = [];

            // this is a normal controller method that is NOT exposed to other directives
            $scope.startGallery = function() {
            };

            // this method will be exposed to directives that require imageSlider
            this.addSlide = function(slide) {
                $scope.slides.push( slide );
            }
        }
    };
} ]);


app.directive('slide', [ '$log', function($log) {
    return {
        require: "^imageSlider",
        link: function($scope, elem, attribs, ctrls ) {
            ctrls.addSlide( $scope );
        }
    };
} ] );

This way imageSlider can provide slide an interface to communicate through. Notice the difference in this.functionName vs $scope.functionName. The former being a way to expose methods to other directives.

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138
  • 1
    @StephenRios, this solution does work, but there is a MUCH simpler way of doing this. I am posting you a reply right now. – Kousha Aug 27 '14 at 21:01