AngularJS извлекает данные через AJAX до запуска Директивы - программирование
Подтвердить что ты не робот

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

4b9b3361

Ответ 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 данные.