У меня есть служба AngularJS, которую я хочу инициализировать с некоторыми асинхронными данными. Что-то вроде этого:
myModule.service('MyService', function($http) {
var myData = null;
$http.get('data.json').success(function (data) {
myData = data;
});
return {
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}
};
});
Очевидно, что это не сработает, потому что если что-то попытается вызвать doStuff()
до того, как myData
вернется, я получу исключение нулевого указателя. Насколько я могу судить по прочтению некоторых других вопросов, заданных здесь и здесь, у меня есть несколько вариантов, но ни один из них не кажется очень чистым (возможно, я что-то упускаю):
Настройка службы с помощью «Выполнить»
При настройке моего приложения сделайте это:
myApp.run(function ($http, MyService) {
$http.get('data.json').success(function (data) {
MyService.setData(data);
});
});
Тогда мой сервис будет выглядеть так:
myModule.service('MyService', function() {
var myData = null;
return {
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}
};
});
Это работает некоторое время, но если асинхронные данные занимают больше времени, чем требуется для инициализации всего, я получаю исключение нулевого указателя при вызове doStuff()
Используйте обещанные объекты
Это, вероятно, сработает. Единственный недостаток - везде, где я звоню MyService, я должен знать, что doStuff () возвращает обещание, и весь код будет у насthen
взаимодействовать с обещанием. Я предпочел бы просто подождать, пока myData вернется, прежде чем загружать мое приложение.
Ручная начальная загрузка
angular.element(document).ready(function() {
$.getJSON("data.json", function (data) {
// can't initialize the data here because the service doesn't exist yet
angular.bootstrap(document);
// too late to initialize here because something may have already
// tried to call doStuff() and would have got a null pointer exception
});
});
Глобальный Javascript Var Я мог бы отправить свой JSON напрямую в глобальную переменную Javascript:
HTML:
<script type="text/javascript" src="data.js"></script>
data.js:
var dataForMyService = {
// myData here
};
Тогда это будет доступно при инициализации MyService
:
myModule.service('MyService', function() {
var myData = dataForMyService;
return {
doStuff: function () {
return myData.getSomeData();
}
};
});
Это тоже будет работать, но тогда у меня есть глобальная переменная javascript, которая плохо пахнет.
Это мои единственные варианты? Один из этих вариантов лучше, чем другие? Я знаю, что это довольно длинный вопрос, но я хотел показать, что я попытался изучить все мои варианты. Любое руководство будет с благодарностью.
источник
$http
, затем сохранение данных в службе, а затем загрузка приложения.Ответы:
Вы смотрели на
$routeProvider.when('/path',{ resolve:{...}
? Это может сделать подход к обещанию немного чище:Выставьте обещание в вашем сервисе:
Добавьте
resolve
к вашему маршруту конфигурации:Ваш контроллер не будет создан до разрешения всех зависимостей:
Я сделал пример на plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview
источник
resolve
свойство контроллеру, который не упомянут в$routeProvider
. Например,<div ng-controller="IndexCtrl"></div>
. Здесь контроллер явно упоминается и не загружается через маршрутизацию. В таком случае, как тогда задержать создание экземпляра контроллера?Основанное на решении Мартина Аткинса, вот полное и сжатое чисто угловое решение:
Это решение использует самовыполняющуюся анонимную функцию для получения службы $ http, запроса конфигурации и внедрения ее в константу с именем CONFIG, когда она становится доступной.
Полностью, мы ждем, пока документ не будет готов, и затем загружаем приложение Angular.
Это небольшое улучшение по сравнению с решением Мартина, которое откладывает получение конфигурации до тех пор, пока документ не будет готов. Насколько я знаю, нет причин откладывать вызов $ http для этого.
Модульное тестирование
Примечание. Я обнаружил, что это решение не работает при модульном тестировании, когда код включен в ваш
app.js
файл. Причина этого в том, что приведенный выше код запускается сразу после загрузки файла JS. Это означает, что тестовый фреймворк (в моем случае Jasmine) не имеет возможности предоставить имитационную реализацию$http
.Мое решение, которое меня не совсем устраивало, состояло в том, чтобы перенести этот код в наш
index.html
файл, чтобы инфраструктура модульного тестирования Grunt / Karma / Jasmine его не видела.источник
Я использовал подход, аналогичный описанному @XMLilley, но хотел иметь возможность использовать сервисы AngularJS, такие как
$http
загрузка конфигурации и дальнейшая инициализация без использования низкоуровневых API или jQuery.Использование
resolve
на маршрутах также не было вариантом, потому что мне нужно, чтобы значения были доступны в качестве констант при запуске моего приложения, даже вmodule.config()
блоках.Я создал небольшое приложение AngularJS, которое загружает конфигурацию, устанавливает их как константы в реальном приложении и загружает их.
Посмотреть его в действии (используя
$timeout
вместо$http
) здесь: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=previewОБНОВИТЬ
Я бы рекомендовал использовать подход, описанный ниже Мартином Аткинсом и JBCP.
ОБНОВЛЕНИЕ 2
Поскольку это было необходимо в нескольких проектах, я только что выпустил модуль bower, который позаботится об этом: https://github.com/philippd/angular-deferred-bootstrap
Пример, который загружает данные из серверной части и устанавливает константу APP_CONFIG в модуле AngularJS:
источник
В случае «ручной начальной загрузки» можно получить доступ к службам Angular, вручную создав инжектор перед начальной загрузкой. Этот начальный инжектор будет автономным (не привязанным к каким-либо элементам) и будет включать только подмножество загруженных модулей. Если все, что вам нужно, это основные сервисы Angular, достаточно просто загрузить их
ng
, например так:Например, вы можете использовать
module.constant
механизм, чтобы сделать данные доступными для вашего приложения:Это
myAppConfig
теперь может быть введена так же , как и любой другой сервис, и , в частности , он доступен на этапе конфигурации:или, для небольшого приложения, вы можете просто внедрить глобальную конфигурацию непосредственно в вашу службу, за счет распространения знаний о формате конфигурации по всему приложению.
Конечно, поскольку асинхронные операции здесь блокируют загрузку приложения и, таким образом, блокируют компиляцию / компоновку шаблона, целесообразно использовать
ng-cloak
директиву, чтобы предотвратить появление необработанного шаблона во время работы. Вы могли бы также предоставить некоторую индикацию загрузки в DOM, предоставив некоторый HTML, который отображается только до инициализации AngularJS:Я создал полный рабочий пример этого подхода на Plunker, загружая конфигурацию из статического файла JSON в качестве примера.
источник
У меня была та же проблема: я люблю
resolve
объект, но это работает только для содержания ng-view. Что если у вас есть контроллеры (скажем, для навигации верхнего уровня), которые существуют за пределами ng-view и которые необходимо инициализировать данными, прежде чем маршрутизация даже начнет происходить? Как мы можем избежать обмана на стороне сервера только для того, чтобы заставить это работать?Используйте ручную загрузку и угловую постоянную . Наивный XHR получает вам ваши данные, и вы загружаете его в свой обратный вызов, который решает ваши проблемы с асинхронностью. В приведенном ниже примере вам даже не нужно создавать глобальную переменную. Возвращенные данные существуют только в угловой области как инъекционные, и даже не присутствуют внутри контроллеров, служб и т. Д., Если вы не внедрили их. (Как бы вы ни вводили вывод вашего
resolve
объекта в контроллер для маршрутизируемого представления.) Если вы после этого предпочитаете взаимодействовать с этими данными как сервисом, вы можете создать сервис, внедрить данные, и никто никогда не станет мудрее. ,Пример:
Теперь ваша
NavData
константа существует. Идите вперед и введите его в контроллер или службу:Конечно, использование голого XHR-объекта лишает вас многих тонкостей, которые
$http
JQuery позаботится о вас, но этот пример работает без особых зависимостей, по крайней мере, для простогоget
. Если вы хотите немного больше мощности для вашего запроса, загрузите внешнюю библиотеку, чтобы помочь вам. Но я не думаю, что$http
в этом контексте можно получить доступ к угловым или другим инструментам.(ТАК связанный пост )
источник
В вашем .config для приложения вы можете создать объект разрешения для маршрута и передать функцию в $ q (объект обещания) и имя службы, от которой вы зависите, и разрешить обещание в Функция обратного вызова для $ http в сервисе выглядит так:
МАРШРУТ КОНФИГ
Angular не будет отображать шаблон или делать контроллер доступным, пока не будет вызван defer.resolve (). Мы можем сделать это в нашем сервисе:
СЛУЖБА
Теперь, когда MyService имеет данные, назначенные его свойству data, и обещание в объекте разрешения маршрута разрешено, наш контроллер для маршрута запускается, и мы можем назначить данные из сервиса нашему объекту контроллера.
КОНТРОЛЛЕР
Теперь все наши привязки в области действия контроллера смогут использовать данные, полученные от MyService.
источник
resolve
которому я закончил, за исключением того, что мне нужно было создать объект со свойством, являющимся функцией. так что в итоге это былоresolve:{ dataFetch: function(){ // call function here } }
Поэтому я нашел решение. Я создал сервис angularJS, мы назовем его MyDataRepository и создал для него модуль. Затем я подаю этот файл javascript с моего контроллера на стороне сервера:
HTML:
Серверный:
Затем я могу добавить MyDataRepository туда, где мне это нужно:
Это отлично сработало для меня, но я открыт для любых отзывов, если у кого-то есть. }
источник
Кроме того, вы можете использовать следующие методы для предоставления вашей услуги в глобальном масштабе, прежде чем будут выполнены фактические контроллеры: https://stackoverflow.com/a/27050497/1056679 . Просто разрешите ваши данные глобально, а затем передайте их, например, в службу
run
.источник
Ты можешь использовать
JSONP
для асинхронной загрузки служебных данных. Запрос JSONP будет сделан во время начальной загрузки страницы, и результаты будут доступны до запуска вашего приложения. Таким образом, вам не придется раздувать вашу маршрутизацию с избыточными разрешениями.Ваш HTML будет выглядеть так:
источник
Самый простой способ получить любую инициализацию - использовать каталог ng-init.
Просто поместите область видимости ng-init, где вы хотите получить данные инициализации
index.html
index.js
источник