Запускать контроллеры только после завершения инициализации в AngularJS
У меня есть некоторые глобальные данные, которые необходимо загрузить перед выполнением любого контроллера в моем приложении AngularJS (т.е. разрешить зависимости глобально в AngularJS).
Например, у меня есть UserService
с getCurrentUser()
метод, который выполняет запрос к внутреннему серверу для получения данных о текущем аутентифицированном пользователе. И у меня есть контроллер, которому нужны эти данные для запуска еще одного запроса (например, для загрузки баланса пользователя).
Как я могу этого достичь?
1 ответ
Обновить
Please consider using method specified in the " https://blog.mariusschulz.com/2014/10/22/asynchronously-bootstrapping-angularjs-applications-with-server-side-data " article if possible.
You can use the https://github.com/philippd/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
вариант 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.
Я бы порекомендовал использовать этот подход. 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
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. Делать обещания в корневом контроллере
Вы можете давать обещания и добавлять их в $rootScope
внутри вашего корневого контроллера, т.е. run()
функция.
Я создал Plunk для демонстрации идеи: http://plnkr.co/edit/gpguG5Y2S4KOz1KOKzXe?p=preview
Это отлично работающее решение, однако, оно раздувает код и усложняет его использование и понимание (ад обратного вызова). Я бы порекомендовал это, только если первый подход не работает для вас.
3. Передача данных со страницы приложения
Вы можете включить все данные инициализации непосредственно в HTML-страницу, созданную на сервере, и получить к ней доступ из своего приложения.
Рассмотрим этот пример:
<html>
<body>
<script src="application.js"></script>
<script type="text/javascript">
application.init({
// Pass your data here.
userData: { ... }
});
</script>
</body>
</html>
И вы можете загрузить приложение AngularJS вручную в init()
метод вашего обычая application
объект.
Мне не очень нравится этот подход, так как я считаю, что веб-интерфейс и веб-приложение должны быть сильно разделены. В идеале, ваш интерфейс должен быть статическим веб-сайтом (например, набор HTML, CSS и JS, который может быть доставлен через CDN), а ваш сервер должен быть строго сервером API без уровня представления (то есть он не должен ничего знать о HTML, CSS и например). Тем не менее, это рабочее решение, если вы можете жить в тесной интеграции между компонентами приложения.