Bhargav Ponnapalli
Bhargav Ponnapalli

Reputation: 9412

Run jQuery after Angular is done rendering

I am trying to populate a profile page with a json object in angularjs. I am using directives for this. I have a profile directive which has profile-section directives as children. Profile section has profile sub-section directives as children. I need to run a snippet just before angular has started compiling and just after angular has finished rendering the template.

I tried

app.run()
$timeout
$evalAsync
$(document).ready()
$scope.$broadcast
postLink function

This is a skeleton of my code

    var app = angular.module("profile",[]);

app.controller("profileController",['$scope',function($scope){
    var ctrl = this;


}])

.controller("profileSectionController",['$scope',function($scope){
    //$scope.$emit('dataloaded');

}])


.directive("profile",[function(){
    return {
        transclude:true,
        restrict:'EA',
        replace:true,
        templateUrl:'/sstatic/angular_templates/de/profile.html',
        scope:{
            person:"="
        },
        controller:'profileController',
        compile:function(elem,attrs,transclude){
            return {
                pre : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);
                //$(elem).css({"display":"none"});

                },
                post : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);
                    //$(elem).css({"display":"block"});



                }
            }
        }
    }
}])
.directive("profileSection",[function(){
    return {
        transclude:true,
        restrict:'EA',
        replace:true,
        require:'^profile',
        templateUrl:'/sstatic/angular_templates/de/profile-section.html',
        scope:{
            title:"@",
            right:"=",
            sub:"="
        },
        controller:"profileSectionController",
        compile:function(elem,attrs,transclude){
            return {
                pre : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);

                },
                post : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);


                }
            }
        }
    }
}])
    .directive("profileSub",[function(){
    return {
        transclude:true,
        restrict:'EA',
        replace:true,
        require:'^profile',
        templateUrl:'/sstatic/angular_templates/de/profile-sub-section.html',
        scope:{
            subsection:"="
        },
        controller:function(){

        },
        compile:function(elem,attrs,transclude){
            return function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);


                }
        }
    }
}])

However, most of them fire after profile directive is loaded, but not after its children have loaded. I cannot attach it to the children because, it will fire too many times.

This is the expected timeline of events.

Start Render Event Fires
Profile Linked 
Profile Section 1 Linked
Profile Sub Section 1 Linked
Profile Sub Section 2 Linked
Profile Section 2 Linked 
Profile Sub Section 1 Linked
Profile Sub Section 2 Linked
Profile Sub Section 3 Linked
....
End Render Event Fires

This is how it happens now.

Start Render Event Fires
Profile Linked 
End Render Event Fires
Profile Section 1 Linked
Profile Sub Section 1 Linked
Profile Sub Section 2 Linked
Profile Section 2 Linked 
Profile Sub Section 1 Linked
Profile Sub Section 2 Linked
Profile Sub Section 3 Linked
....

I need some way of running a script after every single part of angular is done loading in the DOM.

Please help. Much appreciated.

Upvotes: 6

Views: 13621

Answers (2)

Bhargav Ponnapalli
Bhargav Ponnapalli

Reputation: 9412

First of all, Many thanks to @pixelbits.

I understood how directive loading works. Based on pixelbits' answer what I did was,

  • When a grand-child subsection loads I tell the Profile Directive through an emit event, that a new subsection has arrived into the page.
  • After the subsection has rendered, I use $timeout to emit another event to tell the Profile Directive that this subsection was rendered.

Since compiling occurs before rendering, I can check the renderedCount in the Profile Directive and when it equals the childCount, I can be sure that every grand child has rendered. This is when I trigger the jquery code I need.

Final Snippet

var app = angular.module("profile",[]);

app.controller("profileController",['$scope',function($scope){
    var ctrl = this;


}])

.controller("profileSectionController",['$scope',function($scope){


}])
.controller("profileSubSectionController",['$scope',function($scope){
        //I emit an event telling the parent Profile directive to tell that a new sub section is in the page.
        $scope.$emit("compiled");
    }])

.directive("profile",[function(){
    return {
        transclude:true,
        restrict:'EA',
        replace:true,
        templateUrl:'/sstatic/angular_templates/de/profile.html',
        scope:{
            person:"="
        },
        controller:'profileController',
        compile:function(elem,attrs,transclude){
            return {
                pre : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);
                  //this runs before everything in this chain
                  $(elem).css({"display":"none"});

                },
                post : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);




                      //I count the profileSubSection children here
                       var childCount = 0;
                        scope.$on("compiled",function(msg){
                            childCount++;
                            console.log(childCount);
                        });


                        //I check if all the profile subsections have rendered. If yes I run the script.
                        var renderedCount = 0;
                        scope.$on("rendered",function(msg){
                            renderedCount++;
                            if(renderedCount<childCount){

                            }else{
                                //this runs after everything
                                console.log("now showing profile");
                                $(".loading").hide();
                                $(elem).css({"display":"block"});
                            }

                        });


                }
            }
        }
    }
}])
.directive("profileSection",[function(){
    return {
        transclude:true,
        restrict:'EA',
        replace:true,
        require:'^profile',
        templateUrl:'/sstatic/angular_templates/de/profile-section.html',
        scope:{
            title:"@",
            right:"=",
            sub:"="
        },
        controller:"profileSectionController",
        compile:function(elem,attrs,transclude){
            return {
                pre : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);

                },
                post : function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);


                }
            }
        }
    }
}])
    .directive("profileSub",[function(){
    return {
        transclude:true,
        restrict:'EA',
        replace:true,
        require:'^profile',
        templateUrl:'/sstatic/angular_templates/de/profile-sub-section.html',
        scope:{
            subsection:"="
        },
        controller:"profileSubSectionController",
        compile:function(elem,attrs,transclude){
            return function link(scope,elem,attrs,ctrl){
                    //angular.element(elem).find(".candidate-name").append(scope.person.name);
                       $timeout(function(){
                            console.log("subsection loaded");
                             //Now the sub section emits another event saying that it has been rendered. 
                            scope.$emit("rendered");
                        });



                }
        }
    }
}])

Upvotes: 1

Michael Kang
Michael Kang

Reputation: 52847

Before Compilation

app.run(function() {
  ...
});

After Compilation Before Linking

app.controller('ctrl', function($scope) {
   ...
});

After Linking Before Rendering

app.directive('body', function() {
    return {
       restrict: 'E',
       link: function(scope, element, attr) {
          ... 
       }
    }
});

After Rendering

app.directive('directive', function($timeout) {
    return {
       link: function(scope, element, attr) {
          $timeout(function() {
             ...
          });
       }
    }
});

Upvotes: 21

Related Questions