Javascript AngularJS 在指令运行之前通过 AJAX 检索数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14527583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AngularJS retrieve data via AJAX before Directive runs
提问by jacob
I'm using AngularUI's uiMapdirectives to instantiate a google map. The uiMap directive works great with hard-coded data ({mapOptions}and [myMarkers]); however I run into trouble when I retrieve this data via $http.get()(the directive fires before the AJAX call has finished).
我正在使用 AngularUI 的uiMap指令来实例化一个谷歌地图。uiMap 指令适用于硬编码数据({mapOptions}和[myMarkers]);但是,当我通过检索此数据时遇到了麻烦$http.get()(指令在 AJAX 调用完成之前触发)。
Initially I was executing the GET in my GoogleMapscontroller, but when I realised things were happening out of sequence, I moved the GET into the uiMapdirective. I've got 2 problems with this:
最初我在我的GoogleMaps控制器中执行 GET ,但是当我意识到事情发生的顺序不正确时,我将 GET 移动到uiMap指令中。我有两个问题:
- I think this is not the correct way to do this.
- The GET also retrieves the data for
[myMarkers]- The function/directive that creates the markers is ubiquitous in that it is responsible for creating all overlays
- 我认为这不是正确的方法。
- GET 还检索数据
[myMarkers]- 创建标记的函数/指令无处不在,因为它负责创建所有覆盖
So my question is, is there somewhere else in the application where I can retrieve the data (and apply it to scope) before the directive runs?
所以我的问题是,在指令运行之前,应用程序中是否还有其他地方可以检索数据(并将其应用于范围)?
I read up on $q, and that kind of sounds like what I want, but I'm not sure if I can do it within my controller rather than in the directive (also not sure how $q.defer.resolve()is any different than $http.success()).
我阅读了$q,这听起来像我想要的,但我不确定我是否可以在我的控制器而不是指令中做到这一点(也不确定与$q.defer.resolve()有什么不同$http.success())。
EDITMost of the code I'm using is copy/paste from AngularUI's doc, but here's a plunk: http://plnkr.co/edit/t2Nq57
编辑我使用的大部分代码是从 AngularUI 的文档中复制/粘贴的,但这里有一个 plunk:http://plnkr.co/edit/t2Nq57
Solution
解决方案
Based on Andy's answer, I used a combination of uiMap and 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 1uiIf cannot be in the same element that specifies the controller furnishing its condition (uiIf has higher priority than ngController, so its controller won't get set before uiIf executes).
警告 1uiIf 不能位于指定控制器提供其条件的同一元素中(uiIf 的优先级高于 ngController,因此在 uiIf 执行之前不会设置其控制器)。
Caveat 2Be sure to use the most recent version of uiIf(the version supplied in the most recent tag, v0.3.2, is out of date). The old one has bug causing a TypeError under certain circumstances.
警告 2请务必使用最新版本的 uiIf(最新标签中提供的版本 v0.3.2 已过时)。旧的在某些情况下有导致 TypeError 的错误。
Caveat 3jQuery MUST be included before AngularJS (in index.html); else you will receive a TypeError stating that Object [object Object] has no method 'trigger'(or Object [object HTMLDivElement] has no method 'trigger'on Windows). Chrome will allow you to step into the trigger function because Chrome knows about it, but Angular does not (and Angular is throwing the error).
警告 3jQuery 必须包含在 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{}
DrawbackuiMap does not currently support rendering makers on domready. I'm looking into an alternative version of uiMapMarker suggested in this GitHub issue / comment.
Solution to this issue: https://stackoverflow.com/a/14617167/758177
Working example: http://plnkr.co/edit/0CMdW3?p=preview
缺点uiMap 目前不支持 domready 上的渲染器。我正在研究此GitHub 问题/评论中建议的 uiMapMarker 替代版本。
此问题的解决方案:https://stackoverflow.com/a/14617167/758177
工作示例:http://plnkr.co/edit/0CMdW3?p = preview
回答by Andrew Joslin
You could just delay execution of ui-map until your data is loaded.
您可以延迟 ui-map 的执行,直到您的数据加载。
HTML:
HTML:
<div ui-if="loadingIsDone">
<div ui-map="myMap" ui-options="myOpts"></div>
</div>
JS:
JS:
$http.get('/mapdata').then(function(response) {
$scope.myOpts = response.data;
$scope.loadingIsDone = true;
});
回答by Ben Lesh
Generally, what you can do is have your directive get set up, start the load and finish in the success. I'm assuming you want to load one piece of data for all instances of your directive. So here's some psuedo-code for how you might want to attack this:
通常,您可以做的是设置您的指令,开始加载并成功完成。我假设您想为指令的所有实例加载一条数据。所以这里有一些伪代码,用于说明您可能想要如何攻击它:
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's 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.';
})
}
}
}]);
Anyhow, I hope that helps.
无论如何,我希望这会有所帮助。
回答by Ben Felda
I am not sure if this will help without seeing code, but I ran into this same issue when I was creating my $scope.markersobject inside the $http.successfunction. I ended up creating the $scope.markers = []before the $httpfunction, and inside the .successfunction, I populated the $scope.markersarray with the return data.
我不确定这是否会在没有看到代码的情况下有所帮助,但是当我$scope.markers在$http.success函数中创建对象时遇到了同样的问题。我最终$scope.markers = []在$http函数之前创建了,在函数内部.success,我$scope.markers用返回数据填充了数组。
So the $scope object was bound while the directive was compiling, and updated when the data returned.
所以 $scope 对象在指令编译时被绑定,并在数据返回时更新。
[UPDATE SUGGESTION]
[更新建议]
Have you tried taking advantage resolvein your route?
您是否尝试过利用resolve您的路线?
function($routeProvider) {
$routeProvider.
when(
'/',{
templateUrl: 'main.html',
controller: Main,
resolve: {
data: function(httpService){
return httpService.get()
}
}
}).
otherwise({redirectTo: '/'});
}
I usually put my $http requests in a service, but you could call the $http right from your route:
我通常把我的 $http 请求放在一个服务中,但你可以直接从你的路由中调用 $http :
App.factory('httpService'), function($http){
return {
get: function(){
$http.get(url)
}
}
});
Then, in your controller, inject dataand set your $scopeitems to the data.
然后,在您的控制器中,注入data您的$scope项目并将其设置为数据。

