Thursday 13 June 2019

javascript - Run controllers only after initialization is complete in AngularJS




I have some global data that needs to be loaded before any controller is executed in my AngularJS application (i.e. resolve dependencies globally in AngularJS).



For example, I have a UserService with the getCurrentUser() method which does a request to the backend server in order to get data about the currently authenticated user. And I have a controller that needs this data in order to launch yet another request (for example to load user's balance).



How can I achieve that?


Answer





Please consider using method specified in the «Asynchronously Bootstrapping AngularJS Applications with Server-Side Data» article if possible.




You can use the angular-deferred-bootstrap module to achieve that now!






I'm not sure about validity of this answer anymore, you can still use the ideas, but be sure to properly test it with your actual code. I will try to keep this answer up to date with never technologies.





There are several approaches to the problem of asynchronous application initialization.




When it comes to data that must be resolved before a single controller is called - you can easily use resolve option of ngRoute's $routeProvider. However, when you need some global data to be loaded before ANY controller is called - you have to improvise.



I've tried to collect all possible solutions in this answer. I'm providing them in the order of preference.



1. Using ui-router



When using ui-router instead of native ngRoute you can create an abstract root state and resolve all data in it, before sub-states are activated.



I would recommend to use this approach. ui-router provides a lot of additional features including ability to resolve dependencies hierarchically and is well accepted by the developer community.




Example



module.config(function($urlRouterProvider, stateHelperProvider) {
$urlRouterProvider.otherwise('/404');
stateHelperProvider.setNestedState({
name: 'root',
template: '',
abstract: true,
resolve: {
user: function(UserService) {

// getCurrentUser() returns promise that will be resolved
// by ui-router before nested states are activated.
return UserService.getCurrentUser();
}
},
children: [{
name: 'index',
url: '/',
templateUrl: '/partials/index'
}, {

name: 'not-found',
url: '/404',
templateUrl: '/partials/404'
}, {
name: 'balance',
url: '/balance',
templateUrl: '/partials/balance',
resolve: {
balance: function(UserService, user) {
// Using data resolved in parent state.

return UserService.getBalanceByAccountId(user.accountId);
}
}
}]
});
});


The stateHelper will help greatly to reduce the code when using abstract root scope approach.




Root scope is defined as abstract so can not be activated directly and it has no URL.



template: '' is required for nested views to be properly rendered.



2. Making promises in root controller



You can make promises and add them to the $rootScope inside of your root controller, i.e. run() function.



I've created a Plunk to demonstrate the idea:
http://plnkr.co/edit/gpguG5Y2S4KOz1KOKzXe?p=preview




This is a perfectly working solution, however, it bloats the code and makes it harder to use and understand (callback hell). I would recommend it only if the first approach is not working for you.



3. Passing data with the application page



You can include all initialization data directly to the HTML page generated on the server and access it from your application.



Consider this example:

















And you can bootstrap AngularJS application manually in the init() method of your custom application object.



I don't really like this approach, as I do believe that frontend and backend of Web application should be highly separated. Ideally, your frontend should be a static website (e.g. bunch of HTML, CSS and JS that can be delivered via CDN) and your backend should be a strictly an API server without a presentation layer (i.e. it should know nothing about HTML, CSS and such). However, it's a working solution if you can live with tight integration between application components.


No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...