Method on $viewcontentloaded is firing asynchronously. To detail my problem, I have a variable in root scope i.e. my Main controller, which need to be initialized before my view controller loads. In module.run I am calling a sync function to initialize $rootScope.session. And In my view controller of a route, I am checking the status of session in afunction that is called like
$scope.$on('$viewContentLoaded', function() {
$scope.initialize();
});
But some times on page refreash, I am getting an undefined value for $rootScope.session, as It may have initialized later. So, Is there any way to make this synchronous like rootscope will be initialized before view loads. And for curiosity, how it will affect, if I call the $scope.initialize(); normally in my controller, in $viewContentLoaded or in $routeChangeSuccess.
Thanks in advance.
So, Is there any way to make this synchronous like rootscope will be initialized before view loads.
Use the $controller service to manually create the controller, as in a unit test.
$controllerProvider.register('FooCtrl', FooCtrl);
ctrl = $controller('FooCtrl', {$scope: scope});
Or $broadcast a custom event from the main controller down to the child:
function mainCtrl($rootScope)
{
$rootScope.$broadcast('abc');
}
function secondCtrl($scope)
{
$scope.$on('abc', function(event) { $scope.initialize(); });
}
Or use a try/catch block and a recursive call with a timer.
These are more or less the steps that you would take to implement lazy loading in AngularJS. In summary, you would first define your app module to keep instances of the relevant providers. Then you would define your lazy artifacts to register themselves using the providers rather than the module API. Then using a ‘resolve’ function that returns a promise in your route definition, you would load all lazy artifacts and resolve the promise once they have been loaded. This ensures that all lazy artifacts will be available before the relevant route is rendered. Also, don’t forget to resolve the promise inside $rootScope.$apply, if the resolution will be happening outside of AngularJS. Then you would create a ‘bootstrap’ script that first loads the app module before bootstrapping the app. Finally, you would link to the bootstrap script from your ‘index.html’ file.
References
AngularJS source: controllerSpec.js
Ifeanyi Isitor: Lazy Loading In AngularJS
AngularJS Lazy Loading with Require.js
Split Large AngularJS Controllers using the Mixin Pattern
Related
I have a custom scope method within a Controller, and when a custom directive loads, I want to run a method inside the defined controller. I've seen a lot of options, but which one could be referenced inside a template via an ng-* call? Otherwise, what are the best options?
Since the controller is instantiated when the directive is loaded, any method called in your controller will be called on page load. In my code it is often something like
angular.module('app')
.controller('controllerName', ctrl);
function ctrl() {
/*--------Initialize--------*/
someMethod()
}
If you are on angular 1.5 already and can use the new component way to build your custom directive, you could use the newly introduced $onInit method instead of polluting the constructor, that should only initalize the object itself.
For details, please see this blogpost: https://toddmotto.com/on-init-require-object-syntax-angular-component/
I have SPA and on the first page I load a big data object from the REST service.
The first page consists of the main part which resolved by controller, set of directives in the current scope which render some parts of received object and a header directive in the $rootscope which also render some part of received data.
I call API in the controller and when all data will be loaded I should notify about it all related directives for rendering loaded data.
Now I use $watch() and $watchGroup() for the same scope directives and $rootScope.$broadcast() for the header from the $rootscope.
Is there any more gracefully solution for it?
What is the best way to do this?
This sounds like a good use case for ngResource, which is an official Angular module for REST resources. I'd recommend you create a service for your resource like:
app.factory('Widget', function ($resource) {
return $resource('/api/v1/widgets/:id');
});
Then you can use it in your controller to handle the loading of your data.
app.controller('WidgetController', function ($scope, Widget) {
$scope.widgets = Widget.query({active: true});
});
In your views/templates/whatever, can bind right to widgets, which will be an array of widgets that match the query. The array will be empty while the widgets load, then it will be populated with the results -- and the binding will automatically update.
You can also bind to widgets.$resolved which (essentially) indicates whether the resource has finished loading or not.
Check out more about ngResource here.
I'm trying to come up with some code which allows me to run a function in the controller but only once the whole dom is setup and ready (including the directives link function run etc.).
I'm currently communicating between ctrl/service and the directive via $rootScope broadcasts. The first broadcast at the time of the controller loading is not being picked up by the directive. The reason is of course that the controller loads before the directive link function runs. I've read a few similar questions on SO where people recommended on using $timeout for these calls. This unfortunately doesn't always work and I don't want to clutter my ctrl/services with lots of $timeout calls. Therefore I'm looking for another solution to my problem.
Communication pattern is as follows:
1.) Controller tells Service to prepare some data (via function call in service)
2.) Service tells directive to display data (via broadcast)
3.) Directive displays data ...or not in my case :(
EDIT:
As timing is essential in my app, I'm basically looking for a way to initiate a function in the controller as soon as all angular components have finished loading. That function in the controller will display content by assigning a value to a scope variable. At the same time it will start taking the time. I can of course only start doing that once the directives are loaded, otherwise the tmining is wrong or the directive is not yet ready to display content etc.
I've read through a blog post by Ben Nadel, which basically shows how directives are loaded. I was hoping to setup an outer directive which loads last so I can trigger the finished loading from there. Unfortunately that doesn't work as soon as any of the inner directives use a templateUrl.
http://www.bennadel.com/blog/2603-directive-controller-and-link-timing-in-angularjs.htm
Using $timeout would be terrible. Don't do that. You can't define how long a server call is going to take.
I would recommend using this pattern:
Have the controller use a service to load some data, and have the
promise in the controller assign the return data to a scope variable.
Pass that scope variable into your directive.
Setup a watch in the directive link function, when it loads it will go from undefined to desired value. Done!
// in your controller
YourService.all().then(function(data) {
$scope.data = data;
});
// in your view
<some-directive your-data="data"></some-directive>
// in your directive
angular.module('blah.directives').directive('someDirective', function() {
return {
scope: {
yourData: '='
},
link: function(scope, element, attrs) {
var watcher = scope.$watch('yourData', function() {
if(scope.yourData === undefined) return;
// at this point your data will be loaded, do work
// optionally kill watcher at this point if it's not going to update again
watcher();
});
}
}
});
I have a bit of a problem with an Angular application. Basically I want to manipulate the scope outside of the application itself. If I type in the console:
$scope = angular.element($('.lead-system')).scope()
I can use $scope with $apply and it all works fine. However, when I use this in a rails coffeescript file:
angular.element(document).ready ->
console.log angular.element($('.lead-system')).scope()
It logs undefined and of course I can't use $apply with that.
What's up with that? I'm loading the JS files in this order:
app.js <-- Main angular app initializer
pending.js.coffee <-- The file listed above
lead_controller.js <-- The controller of the scope I'm trying to access
Wrap it in the $timeout, just to check if it will work then. That should tell you if this can be fixed by different timing or give you ideas for further exploration.
My recommendation though would be to fire an event (emit/broadcast or even arbitrary external thing in your case) in controller and in reponse to that event manipulate the scope. You could even pass the scope as parameter for convenience.
I have an issue where i want fire a method from a service immediately after a route change. That method has to lookup an object on a Firebase:
app.service('document',function($q,$firebase){
var databaseReference = new Firebase(firebase),
database = $firebase(databaseReference);
return{
getDocument: function(title){
return database.documents[title];
}
}
});
unfortunately, the documents property won't load directly; it takes a few ms to appear.
I know that i could wrap this up in a promise, but how can i make the service being returned only when that promise is resolved?
If you use Angular ui-router, you can put one or more promises in a resolve block of the same state your controller is in, and declare those as dependencies to be injected into your controller. The controller then does not get created until all those promises have been resolved, and within your controller you'll have access to the resolved promises. See https://github.com/angular-ui/ui-router/wiki#resolve for more info.