6

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.

7
  • I think you need Lazy loading Commented Apr 14, 2015 at 6:39
  • What do you mean by "started loading"? do you mean before compilation has started? Commented Apr 14, 2015 at 6:39
  • Have you tried $broadcast. Refer dotnet-tricks.com/Tutorial/angularjs/… Commented Apr 14, 2015 at 6:40
  • I think your question is similar to this post stackoverflow.com/questions/16935766/… Commented Apr 14, 2015 at 6:42
  • @Reena : I agree it is similar to that question. However, I have nested directives and the the event fires after the parent directive has rendered html. Child directives hadn't started the rendering at that point. Commented Apr 14, 2015 at 6:44

2 Answers 2

21

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() {
             ...
          });
       }
    }
});
Sign up to request clarification or add additional context in comments.

3 Comments

Where should I use $timeout? If I use it in profile directive, it runs after profile directive has rendered alright but before the children profile section have rendered.
It shouldn't matter where you put it, as long is it is inside a directive's post-link function (as above) - just put it inside of a directive that has access to the content you need.
Thanks for your help. I got an idea after looking at your explanation about compiling and linking and I found a solution. I will post it soon.
1

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



                }
        }
    }
}])

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.