How do I call a controller function after my object has loaded? - angularjs

I'm very new to angularjs, so this may be a very obvious to a lot of you...
I have a "main" view with a "MainCtrl" controller. The controller has a "$scope.init()" function that is being called when the controller is initialised.
However, I have an iFrame in the view that needs to complete it's "onload" js function before the "init" function can successfully run.
I tried triggering the "init" function using the iFrame's ngInit, but that happens before the "onload"
Please can you tell me how to make init function run after the iFrame's onload="otherFunction" has finished?

Created a demo below where ng-init="increaseCount()" increases the value of $scope.carCount variable but only after the <iframe> was loaded.
You can load the <iframe> by clicking on the button and watch how $scope.carCount changes.
Not sure if this is the right way though.
Basically, I have 2 functions inside my INIT function named increaseCount():
A function increaseCount which contains the main INIT logic ie. increasing the amount of cars
A function onIframeLoad which waits until the iframe is loaded and then executes main INIT logic by calling increaseCount();
Interactive DEMO here or run the code below ↓↓↓
var app = angular.module('App', []);
// Allow iframe loading from various sources
app.config(["$sceDelegateProvider", function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads
"self",
// Allow YouTube iframes
"https://www.youtube.com/embed/**"
]);
}]);
// Allow a directive "iframe-onload" in HTML
app.directive('iframeOnload', [function(){
return {
scope: {
callBack: '&iframeOnload'
},
link: function(scope, element, attrs){
element.on('load', function(){
return scope.callBack();
})
}
}}]);
// Main controller
app.controller('MainCtrl', function($scope, $sce) {
$scope.car = 'Mercedes';
$scope.carCount = 0;
$scope.iframeSource = "";
// INIT
$scope.increaseCount = function () {
// First wait on iframe load
$scope.onIframeLoad = function () {
console.log('Iframe fully loaded');
increaseCount(); // If iframe loaded then execute main INIT logic
};
// INIT body - main INIT logic
function increaseCount () {
$scope.$apply('carCount = 10'); // change $scope.carCount to 10
}
};
// Load iframe when clicked on the button
$scope.loadIframe = function () {
console.log("Clicked on the button.");
$scope.iframeSource = "https://www.youtube.com/embed/Ra__OWuOU1M";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="App" ng-controller="MainCtrl" ng-init="increaseCount()">
<h1>{{ car }} count: {{ carCount }}</h1>
<iframe iframe-onload="onIframeLoad()" ng-src="{{ iframeSource }}"></iframe>
<button ng-click="loadIframe()">Load Iframe</button>
</div>

Related

How can I delay the loading of my controller?

How can I delay the loading of my controller till the HTML page has been loaded in AngularJS?
I am using the angular spinner. My code is as below
<span spinner-key="spinner-mySpinner">
<div>...</div>
</span>
I am calling the spinner before calling a promise for loading data.
...
mySpinner.spin('spinner-mySpinner');
loadDataPromise = file.getFileDetails(vm.id, vm.path);
...
loadDataPromise.finally(function () {
mySpinner.stop('spinner-mySpinner');
}
...
I am not able to see the spinner.
I think that my controller is getting initialized before the HTML page gets loaded.
Is there a way in which I can delay my controller till the HTML page has loaded?
You can include your controller functionallity inside a function declared inside of your controller as $scope.func = ... And include a js script in your view that on window.load calls that function of your controller.
angular.module('MyModule', [])
.controller('MyController', function ($scope) {
$scope.myfunction = function () {
//do your stuff
};
});
window.onload = function () {
angular.element(document.getElementById('YourElementId')).scope().myfunction();
}
If your are assigning a controller from the route provider you should set an id to your division where you have the ng-view for example id="ngview" and call your function the following way:
window.onload = function () {
angular.element(document.getElementById('ngview')).scope().myfunction();
}
Hope it helps!

When is Isolated scope bound to parent scope in AngularJs?

At what stage of the compile / link process are variables from the isolated scope of a directive bound to the parent (controller) scope? I have an application in which I want to call a directive api automatically, as soon as the view is loaded.
I understood that scope binding happens in the directive linking phase, so that post linking, the variables exposed on the isolated scope should be available on the parent scope.
However, I find that this is not the case, as demonstrated in the code below (plunker here).
//plunker code
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.buttonClick = function() {
console.log("In buttonClick function, call is: " + this.call);
this.call();
}
$scope.$on("LinkComplete", function(event) {
console.log("In LinkComplete, call is: " + event.currentScope.call);
//event.currentScope.call();
});
console.log("In Constructor, call is: " + this.call);
})
.directive('myDirective', function(){
return {
scope: {
myMethod: '='
},
controller: function ($scope) {
$scope.myMethod = function() {
alert("method called");
};
},
link: function postLink(scope)
{
scope.$emit("LinkComplete");
}
};
});
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div my-directive my-method="call"></div>
<button ng-click="buttonClick()">Call</button>
</body>
Note that the code attempts to access the linked variable (which points to a method on the directive controller) twice during the view initialisation, and on both occasions, the variable is undefined. I wouldn't expect the variable to be available during the main controller constructor, but I would expect it to be available during the post-link event handler. Once the view is loaded, the bound variables are available (click Call button to witness).
How can I access the bound variables from the controller, without requiring the user to click on a button or the like?
That's a good question, when you see some words like 'x is y in z stage' you need to be careful on the accuracy, always dig into the source code to prove it.
Your plunker is using v1.2.27, checkout this line:
https://github.com/angular/angular.js/blob/v1.2.27/src/ng/compile.js#L1492
isolateScope.$watch(function parentValueWatch() {
var parentValue = parentGet(scope);
if (!compare(parentValue, isolateScope[scopeName])) {
// we are out of sync and need to copy
if (!compare(parentValue, lastValue)) {
// parent changed and it has precedence
isolateScope[scopeName] = parentValue;
} else {
// if the parent can be assigned then do so
parentSet(scope, parentValue = isolateScope[scopeName]);
}
}
return lastValue = parentValue;
}, null, parentGet.literal);
This will be evaluated in next $digest cycle and by then parentScope.call will be assigned. At the same time, postLink function is executed synchronously right below it:
https://github.com/angular/angular.js/blob/v1.2.27/src/ng/compile.js#L1575
// POSTLINKING
for (i = postLinkFns.length - 1; i >= 0; i--) {
try {
linkFn = postLinkFns[i];
linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn);
} catch (e) {
$exceptionHandler(e, startingTag($element));
}
}
After postLink has been executed, controller got the event but parentScope.call has not been initialized yet via $digest.
So if you add a setTimeout to check, it looks like what you want:
$scope.$on("LinkComplete", function(event) {
setTimeout(function () {
console.log("In LinkComplete, call is: " + event.currentScope.call);
//event.currentScope.call();
});
});

Angular : ng-init does not run on load

I have seen a few exmaples on stack overflow about this ng-init issue, although I cant seem to find one which references it with the use of a controller.
I have called the function in the controller by having the following in the html file
<div class="tab-container" ng-controller = "ExampleController" ng-init = "init()" >
In the controller:
$scope.init = function(){
alert("do something");
};
It does run, but it runs before the components have loaded on the screen.
Am i missing something?
Thanks
ng-init is supposed to work like this, because it's used to initialize data.
A very simple example:
<ul ng-init="list = [1,2,3,4]">
<li ng-repeat="l in list"></li>
</ul>
If you are trying to run something while your controller loads, it's actually much simpler than you thought:
app.controller('mainCtrl', function ($scope) {
var init = function ($scope) {
// do whatever you need to do to initialize your controller
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
}
init()
})
Or even simpler, if you don't need the function (no closures or whatever)
app.controller('mainCtrl', function ($scope) {
// do whatever you need to do to initialize your controller
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
})
Edit - assuming you're using ngView:
To have the code run on when the page is fully loaded you should set a watcher on the event $viewContentLoaded, like this:
$scope.$on('$viewContentLoaded', function(){
//Here your view content is fully loaded !!
});
app.controller('mainCtrl', function ($scope) {
// This event is triggered when the view has finished loading
$scope.$on('$viewContentLoaded', function() {
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
})
})
another option is using jquery. It would fit if you depend on many elements. But make sure to load jquery with a version of your choice to project.
loading jquery (insert version where it's ...):
<script src="https://code.jquery.com/jquery-..."></script>
the js code:
$(document).ready(function() {
alert("do something");
});

AngularJs event to call after content is loaded

I have a function which I want to call after page content is loaded. I read about $viewContentLoaded and it doesn't work for me. I am looking for something like
document.addEventListener('DOMContentLoaded', function () {
//Content goes here
}, false);
Above call doesn't work for me in AngularJs controller.
According to documentation of $viewContentLoaded, it supposed to work
Emitted every time the ngView content is reloaded.
$viewContentLoaded event is emitted that means to receive this event you need a parent controller like
<div ng-controller="MainCtrl">
<div ng-view></div>
</div>
From MainCtrl you can listen the event
$scope.$on('$viewContentLoaded', function(){
//Here your view content is fully loaded !!
});
Check the Demo
Angular < 1.6.X
angular.element(document).ready(function () {
console.log('page loading completed');
});
Angular >= 1.6.X
angular.element(function () {
console.log('page loading completed');
});
fixed - 2015.06.09
Use a directive and the angular element ready method like so:
js
.directive( 'elemReady', function( $parse ) {
return {
restrict: 'A',
link: function( $scope, elem, attrs ) {
elem.ready(function(){
$scope.$apply(function(){
var func = $parse(attrs.elemReady);
func($scope);
})
})
}
}
})
html
<div elem-ready="someMethod()"></div>
or for those using controller-as syntax...
<div elem-ready="vm.someMethod()"></div>
The benefit of this is that you can be as broad or granular w/ your UI as you like and you are removing DOM logic from your controllers. I would argue this is the recommended Angular way.
You may need to prioritize this directive in case you have other directives operating on the same node.
You can directly call it by adding {{YourFunction()}} after HTML element.
Here is a Plunker Link.
I had to implement this logic while handling with google charts. what i did was that at the end of my html inside controller definition i added.
<body>
-- some html here --
--and at the end or where ever you want --
<div ng-init="FunCall()"></div>
</body>
and in that function simply call your logic.
$scope.FunCall = function () {
alert("Called");
}
var myM = angular.module('data-module');
myM.directive('myDirect',['$document', function( $document ){
function link( scope , element , attrs ){
element.ready( function(){
} );
scope.$on( '$viewContentLoaded' , function(){
console.log(" ===> Called on View Load ") ;
} );
}
return {
link: link
};
}] );
Above method worked for me
you can call javascript version of onload event in angular js. this ng-load event can be applied to any dom element like div, span, body, iframe, img etc. following is the link to add ng-load in your existing project.
download ng-load for angular js
Following is example for iframe, once it is loaded testCallbackFunction will be called in controller
EXAMPLE
JS
// include the `ngLoad` module
var app = angular.module('myApp', ['ngLoad']);
app.controller('myCtrl', function($scope) {
$scope.testCallbackFunction = function() {
//TODO : Things to do once Element is loaded
};
});
HTML
<div ng-app='myApp' ng-controller='myCtrl'>
<iframe src="test.html" ng-load callback="testCallbackFunction()">
</div>
If you're getting a $digest already in progress error, this might help:
return {
restrict: 'A',
link: function( $scope, elem, attrs ) {
elem.ready(function(){
if(!$scope.$$phase) {
$scope.$apply(function(){
var func = $parse(attrs.elemReady);
func($scope);
})
}
else {
var func = $parse(attrs.elemReady);
func($scope);
}
})
}
}
I was using {{myFunction()}} in the template but then found another way here using $timeout inside the controller. Thought I'd share it, works great for me.
angular.module('myApp').controller('myCtrl', ['$timeout',
function($timeout) {
var self = this;
self.controllerFunction = function () { alert('controller function');}
$timeout(function () {
var vanillaFunction = function () { alert('vanilla function'); }();
self.controllerFunction();
});
}]);
Running after the page load should partially be satisfied by setting an event listener to the window load event
window.addEventListener("load",function()...)
Inside the module.run(function()...) of angular you will have all access to the module structure and dependencies.
You can broadcast and emit events for communications bridges.
For example:
module set onload event and build logic
module broadcast event to controllers when logic required it
controllers will listen and execute their own logic based on module onload processes.
If you want certain element to completely loaded, Use ng-init on that element .
e.g. <div class="modal fade" id="modalFacultyInfo" role="dialog" ng-init="initModalFacultyInfo()"> ..</div>
the initModalFacultyInfo() function should exist in the controller.
I found that if you have nested views - $viewContentLoaded gets triggered for every of the nested views. I've created this workaround to find the final $viewContentLoaded. Seems to work alright for setting $window.prerenderReady as required by Prerender (goes into .run() in the main app.js):
// Trigger $window.prerenderReady once page is stable
// Note that since we have nested views - $viewContentLoaded is fired multiple
// times and we need to go around this problem
var viewContentLoads = 0;
var checkReady = function(previousContentLoads) {
var currentContentLoads = Number(viewContentLoads) + 0; // Create a local copy of the number of loads
if (previousContentLoads === currentContentLoads) { // Check if we are in a steady state
$window.prerenderReady = true; // Raise the flag saying we are ready
} else {
if ($window.prerenderReady || currentContentLoads > 20) return; // Runaway check
$timeout(function() {checkReady(currentContentLoads);}, 100); // Wait 100ms and recheck
}
};
$rootScope.$on('$stateChangeSuccess', function() {
checkReady(-1); // Changed the state - ready to listen for end of render
});
$rootScope.$on('$viewContentLoaded', function() {
viewContentLoads ++;
});
var myTestApp = angular.module("myTestApp", []);
myTestApp.controller("myTestController", function($scope, $window) {
$window.onload = function() {
alert("is called on page load.");
};
});
The solution that work for me is the following
app.directive('onFinishRender', ['$timeout', '$parse', function ($timeout, $parse) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
if (!!attr.onFinishRender) {
$parse(attr.onFinishRender)(scope);
}
});
}
if (!!attr.onStartRender) {
if (scope.$first === true) {
$timeout(function () {
scope.$emit('ngRepeatStarted');
if (!!attr.onStartRender) {
$parse(attr.onStartRender)(scope);
}
});
}
}
}
}
}]);
Controller code is the following
$scope.crearTooltip = function () {
$('[data-toggle="popover"]').popover();
}
Html code is the following
<tr ng-repeat="item in $data" on-finish-render="crearTooltip()">
I use setInterval to wait for the content loaded. I hope this can help you to solve that problem.
var $audio = $('#audio');
var src = $audio.attr('src');
var a;
a = window.setInterval(function(){
src = $audio.attr('src');
if(src != undefined){
window.clearInterval(a);
$('audio').mediaelementplayer({
audioWidth: '100%'
});
}
}, 0);

Angular js Div show only for 3 seconds

i am new in angular js. i done with div hide and show. just i want to know how to hide or show div for 3 seconds only.
here i attaching my code which i used.
html code:
<div ng-hide="loginAlertMessage">Dynamic user feedback message comes here.</div>
<a ng-click="forgotPassword()">Forgot Password?</a>
angular js code:
$scope.loginAlertMessage = true;
$scope.forgotPassword = function () {
$scope.loginAlertMessage=false;
};
Inject the $timeout service in the controller and use it to unset loginAlertMessage.
app.controller('MyController', ['$scope', '$timeout', function($scope, $timeout) {
$scope.loginAlertMessage = true;
$scope.forgotPassword = function() {
$scope.loginAlertMessage = false;
$timeout(function() {
$scope.loginAlertMessage = true;
}, 3000);
};
// ...
}]);
inject $timeout service (derived from setTimeout(function() {"action",time(in ms) }) in controller and if wish to display make the div true and if wish to hide make use this service to stop.

Resources