仅在 AngularJS 中完成初始化后运行控制器

2022-01-18 00:00:00 initialization angularjs javascript

在我的 AngularJS 应用程序中执行任何控制器之前,我需要加载一些全局数据(即在 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).

例如,我有一个带有 getCurrentUser() 方法的 UserService,它向后端服务器发出请求,以获取有关当前经过身份验证的用户的数据.我有一个控制器需要这些数据来启动另一个请求(例如加载用户的余额).

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?

推荐答案

更新

请考虑使用 «Asynchronously Bootstrapping AngularJS Applications with Server-Side Data» 文章如果可能的话.

Update

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

您现在可以使用 angular-deferred-bootstrap 模块来实现!

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.

当涉及到必须在调用单个控制器之前解析的数据时 - 您可以轻松地使用 ngRoute$routeProviderresolve 选项代码>.但是,当您需要在调用任何控制器之前加载一些全局数据时 - 您必须即兴发挥.

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.

当使用 ui-router 而不是原生 ngRoute您可以在激活子状态之前创建一个抽象的根状态并解析其中的所有数据.

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.

我建议使用这种方法.ui-router 提供了许多附加功能,包括分层解决依赖关系的能力,并被开发者社区广泛接受.

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.

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);
                }
            }
        }]
    });
});

stateHelper在使用抽象根作用域方法时将极大地帮助减少代码.

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

根范围被定义为抽象,因此不能直接激活,它没有 URL.

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

template: '<ui-view/>' 是正确渲染嵌套视图所必需的.

template: '<ui-view/>' is required for nested views to be properly rendered.

您可以做出承诺并将它们添加到根控制器内部的 $rootScope 中,即 run() 函数.

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

我创建了一个 Plunk 来展示这个想法:http://plnkr.co/edit/gpguG5Y2S4KOz1KOKzXe?p=preview

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.

您可以将所有初始化数据直接包含在服务器上生成的 HTML 页面中,并从您的应用程序中访问它.

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

考虑这个例子:

<html>

<body>

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

</body>

</html>

您可以在自定义 application 对象的 init() 方法中手动引导 AngularJS 应用程序.

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

我不太喜欢这种方法,因为我确实认为 Web 应用程序的前端和后端应该高度分离.理想情况下,您的前端应该是一个静态网站(例如,可以通过 CDN 交付的一堆 HTML、CSS 和 JS),而您的后端应该是一个没有表示层的严格的 API 服务器(即它应该对 HTML、CSS 和这样的).但是,如果您可以在应用程序组件之间进行紧密集成,那么它是一个可行的解决方案.

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.

相关文章