16

AngularUI のuiMapディレクティブを使用して、Google マップをインスタンス化しています。uiMap ディレクティブは、ハードコードされたデータ ({mapOptions}および[myMarkers]) でうまく機能します。ただし、このデータを取得すると問題が発生します$http.get()(AJAX呼び出しが完了する前にディレクティブが起動します)。

最初はGoogleMapsコントローラーで GET を実行していましたが、順序が間違っていることに気付き、GET をuiMapディレクティブに移動しました。これには2つの問題があります:

  1. これは正しい方法ではないと思います。
  2. GET は、次のデータも取得します。[myMarkers]
    • マーカーを作成する関数/ディレクティブは、すべてのオーバーレイの作成を担当するという点で遍在しています。

私の質問は、ディレクティブが実行される前に、データを取得 (およびスコープに適用) できるアプリケーションのどこかに他にあるのでしょうか?

私は$qを読みましたが、それは私が望むもののように聞こえますが、ディレクティブではなくコントローラー内でそれを実行できるかどうかはわかりません (また、$q.defer.resolve()が とどのように異なるかはわかりません$http.success())。

編集私が使用しているコードのほとんどは、AngularUI のドキュメントからのコピー/貼り付けですが、ここにプランクがあります: http://plnkr.co/edit/t2Nq57

解決

Andy's 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>

警告 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 はそれを認識しているため、トリガー関数にステップインできますが、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

通常、できることは、ディレクティブをセットアップしてロードを開始し、成功して終了することです。ディレクティブのすべてのインスタンスに対して 1 つのデータをロードする必要があると想定しています。したがって、これを攻撃する方法の擬似コードを次に示します。

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 に答える