Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
285 views
in Technique[技术] by (71.8m points)

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Update

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.

Old answer

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: '<ui-view/>',
        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: '<ui-view/>' 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:

<html>

<body>

    <script src="application.js"></script>
    <script type="text/javascript">
        application.init({
            // Pass your data here.
            userData: { ... }
        });
    </script>

</body>

</html>

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...