16

我正在使用 AngularUI 的uiMap指令来实例化谷歌地图。uiMap 指令适用于硬编码数据({mapOptions}[myMarkers]);但是,当我通过检索此数据时遇到了麻烦$http.get()(指令在 AJAX 调用完成之前触发)。

最初我在GoogleMaps控制器中执行 GET,但当我意识到事情发生的顺序不正确时,我将 GET 移动到uiMap指令中。我有两个问题:

  1. 我认为这不是正确的方法。
  2. GET 还检索数据[myMarkers]
    • 创建标记的函数/指令无处不在,因为它负责创建所有覆盖

所以我的问题是,在指令运行之前,应用程序中是否还有其他地方可以检索数据(并将其应用于范围)?

我阅读了$q,这听起来像是我想要的,但我不确定我是否可以在我的控制器中而不是在指令中做到这一点(也不确定$q.defer.resolve()与 有什么不同$http.success())。

编辑我使用的大部分代码是从 AngularUI 的文档中复制/粘贴的,但这里有一个 plunk:http ://plnkr.co/edit/t2Nq57

解决方案

根据安迪的回答,我使用了 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>

注意事项1 uiIf 不能位于指定控制器提供其条件的同一元素中(uiIf 的优先级高于 ngController,因此在 uiIf 执行之前不会设置其控制器)。

注意事项2请务必使用最新版本的 uiIf(最新标签 v0.3.2 中提供的版本已过时)。旧版本在某些情况下存在导致 TypeError 的错误。

警告 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 上的渲染制作器。我正在研究这个GitHub 问题/评论中建议的 uiMapMarker 的替代版本。
此问题的解决方案: https
://stackoverflow.com/a/14617167/758177工作示例: http ://plnkr.co/edit/0CMdW3?p=preview

4

3 回答 3

31

您可以延迟执行 ui-map 直到加载数据。

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;
});
于 2013-01-25T19:09:51.380 回答
6

通常,您可以做的是设置指令,开始加载并成功完成。我假设您想为指令的所有实例加载一条数据。所以这里有一些你可能想要如何攻击的伪代码:

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

无论如何,我希望这会有所帮助。

于 2013-01-25T18:56:10.157 回答
1

我不确定这是否会在不查看代码的情况下有所帮助,但是当我在函数中创建$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项目设置为数据。

于 2013-01-25T18:35:22.590 回答