AngularJS извлекает данные через AJAX до запуска Директивы
Я использую директивы AngularUI uiMap для создания копии карты Google. Директива uiMap отлично работает с жестко кодированными данными ({mapOptions}
и [myMarkers]
); однако я столкнулся с трудностями, когда я извлекаю эти данные через $http.get()
(директива срабатывает до завершения вызова AJAX).
Изначально я выполнял GET в моем контроллере GoogleMaps
, но когда я понял, что вещи происходят из последовательности, я переместил GET в директиву uiMap
. У меня есть две проблемы:
- Я думаю, что это не правильный способ сделать это.
- GET также извлекает данные для
[myMarkers]
- Функция/директива, которая создает маркеры, является вездесущей, поскольку она отвечает за создание всех наложений
Итак, мой вопрос: есть ли где-то еще в приложении, где я могу получить данные (и применить их к области видимости) до запуска директивы?
Я прочитал $q, и такие звуки, как то, что я хочу, но я не уверен, могу ли я сделайте это в моем контроллере, а не в директиве (также не знаете, как $q.defer.resolve()
отличается от $http.success()
).
EDIT Большая часть кода, который я использую, копирует/вставляет из документа AngularUI, но здесь plunk: http://plnkr.co/edit/t2Nq57
Решение
На основе Andy answer, я использовал комбинацию uiMap и uiIf
<!-- index.html -->
<div
id="map_container"
ng-controller="GoogleMaps">
<div ui-if="mapReady">
<div
ng-repeat="marker in markers"
ui-map-marker="markers[$index]"
ui-event="{'map-click':'openMarkerInfo(marker)'}"
></div>
<div
ui-map-info-window="myInfoWindow"
ng-include="'infobox.html'"
></div>
<div
id="map_canvas"
ui-map="myMap"
ui-options="mapOptions"
></div>
</div>
</div>
Caveat 1 uiIf не может находиться в том же элементе, который указывает контроллер, снабжающий его условием (uiIf имеет более высокий приоритет, чем ngController, поэтому его контроллер не будет установлен до выполнения uiIf).
Caveat 2 Обязательно используйте самую последнюю последнюю версию uiIf (версия, представленная в последней тег, v0.3.2, устарел). У старого есть ошибка, вызывающая TypeError при определенных обстоятельствах.
Caveat 3 jQuery ДОЛЖЕН быть включен перед AngularJS (в index.html); иначе вы получите TypeError, указав, что Object [object Object] has no method 'trigger'
(или Object [object HTMLDivElement] has no method 'trigger'
в Windows). Chrome позволит вам войти в функцию триггера, потому что Chrome знает об этом, но Angular не делает (и Angular выбрасывает ошибку).
function GoogleMaps( $scope , $http )
{
var mapDefaults = {
center: new google.maps.LatLng(25,-90),//centres on Gulf of Mexico
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
$scope.mapOptions = {};
$scope.mapReady = false;
$scope.markers = [];
$http.get('map.json').then(function mapData(response) {
var map_data = response.data,
user_defaults = map_data.user.defaults; //{center: [lat,lng], zoom: 15}
$scope.mapOptions = {
"center": (typeof user_defaults.center !== 'undefined') ?
new google.maps.LatLng(user_defaults.center[0],user_defaults.center[1])
: mapDefaults.center,
"zoom": (typeof user_defaults.zoom !== 'undefined') ?
parseInt(user_defaults.zoom,10)
: mapDefaults.zoom,
"mapTypeId": mapDefaults.mapTypeId
};
//working on code to populate markers object
$scope.mapReady = true;
});
// straight from sample on http://angular-ui.github.com/#directives-map
$scope.addMarker = function($event) { … };
$scope.openMarkerInfo = function(marker) { … };
$scope.setMarkerPosition = function(marker, lat, lng) { … };
}//GoogleMaps{}
Недостаток. uiMap в настоящее время не поддерживает создателей рендеринга на domready. Я рассматриваю альтернативную версию uiMapMarker, предложенную в этом проблема/комментарий GitHub.
Решение этой проблемы: qaru.site/info/327482/...
Рабочий пример: http://plnkr.co/edit/0CMdW3?p=preview
Ответы
Ответ 1
Вы можете просто отложить выполнение ui-карты, пока ваши данные не будут загружены.
HTML:
<div ui-if="loadingIsDone">
<div ui-map="myMap" ui-options="myOpts"></div>
</div>
JS:
$http.get('/mapdata').then(function(response) {
$scope.myOpts = response.data;
$scope.loadingIsDone = true;
});
Ответ 2
Как правило, вы можете настроить свою директиву, начать загрузку и завершить успешную работу. Я предполагаю, что вы хотите загрузить одну часть данных для всех экземпляров вашей директивы. Итак, вот какой-то псевэ-код для того, как вы можете атаковать это:
app.directive('myDelayedDirective', ['$http', '$q', function($http, $q) {
//store the data so you don't load it twice.
var directiveData,
//declare a variable for you promise.
dataPromise;
//set up a promise that will be used to load the data
function loadData(){
//if we already have a promise, just return that
//so it doesn't run twice.
if(dataPromise) {
return dataPromise;
}
var deferred = $q.defer();
dataPromise = deferred.promise;
if(directiveData) {
//if we already have data, return that.
deferred.resolve(directiveData);
}else{
$http.get('/Load/Some/Data'))
.success(function(data) {
directiveData = data;
deferred.resolve(directiveData);
})
.error(function() {
deferred.reject('Failed to load data');
});
}
return dataPromise;
}
return {
restrict: 'E',
template: '<div>' +
'<span ng-hide="data">Loading...</span>' +
'<div ng-show="data">{{data}}</div>' +
'</div>',
link: function(scope, elem, attr) {
//load the data, or check if it loaded and apply it.
loadData().then(function(data) {
//success! set your scope values and
// do whatever dom/plugin stuff you need to do here.
// an $apply() may be necessary in some cases.
scope.data = data;
}, function() {
//failure! update something to show failure.
// again, $apply() may be necessary.
scope.data = 'ERROR: failed to load data.';
})
}
}
}]);
Во всяком случае, я надеюсь, что это поможет.
Ответ 3
Я не уверен, поможет ли это без просмотра кода, но я столкнулся с этой проблемой, когда создавал свой объект $scope.markers
внутри функции $http.success
. Я закончил создание $scope.markers = []
перед функцией $http
и внутри функции .success
, я заполнил массив $scope.markers
возвращаемыми данными.
Таким образом, объект $scope был связан во время компиляции директивы и обновлялся при возврате данных.
[ПОДТВЕРЖДЕНИЕ ОБНОВЛЕНИЯ]
Вы пытались воспользоваться resolve
на своем маршруте?
function($routeProvider) {
$routeProvider.
when(
'/',{
templateUrl: 'main.html',
controller: Main,
resolve: {
data: function(httpService){
return httpService.get()
}
}
}).
otherwise({redirectTo: '/'});
}
Обычно я помещаю свои $http-запросы в службу, но вы можете называть $http прямо с вашего маршрута:
App.factory('httpService'), function($http){
return {
get: function(){
$http.get(url)
}
}
});
Затем в вашем контроллере введите data
и установите для своих элементов $scope
данные.