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.
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.
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.
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.
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.
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() {
...
});
}
}
});
First of all, Many thanks to @pixelbits.
I understood how directive loading works. Based on pixelbits' answer what I did was,
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");
});
}
}
}
}])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With