Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 954
Bhargav Ponnapalli Avatar asked Apr 14 '15 06:04

Bhargav Ponnapalli


People also ask

Can I use jQuery and Angular together?

Yes, AngularJS can use jQuery if it's present in your app when the application is being bootstrapped. If jQuery is not present in your script path, AngularJS falls back to its own implementation of the subset of jQuery that we call jQLite.

Is jQuery faster than Angular?

Angular is a popular open-source front-end development framework that is based on TypeScript. It is used to create dynamic single-page applications and make testing easier. It is an upgraded version of AngularJs that is Javascript based and is significantly faster.

Is jQuery required for Angular?

jQLite is enough for angular to work. This is to minimize dependencies. Yet, if you load jQuery before angular, then angular will use jQuery. Most of the time, you do not need to use jQuery.

Is Angular better than jQuery?

Angular JS can do everything that JQuery does and even much more. It is easy to write and run unit tests in Angular JS applications. Dependency management is effortless and binding dynamic data is powerful. For building testable web applications, we can use Angular JS than JQuery.


2 Answers

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() {
             ...
          });
       }
    }
});
like image 112
pixelbits Avatar answered Nov 14 '22 20:11

pixelbits


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");
                        });



                }
        }
    }
}])
like image 45
Bhargav Ponnapalli Avatar answered Nov 14 '22 22:11

Bhargav Ponnapalli