Подтвердить что ты не робот

В Angular, как передать объект/массив JSON в директиву?

В настоящее время мое приложение имеет контроллер, который принимает файл JSON, а затем выполняет итерацию через них с помощью "ng-repeat". Все это отлично работает, но у меня также есть директива, которая должна проходить через один и тот же файл JSON. Это создает проблему, поскольку я не могу дважды запрашивать один и тот же файл JSON на одной странице (и не хочу, потому что это будет неэффективно). И запрос на директиву, и контроллер и итерация через данные JSON просто прекрасны, если я изменю имя файла одного из файлов JSON.

Мне интересно: как лучше всего передать массив, сформированный из моего запроса JSON контроллера, в директиву? Как я могу передать массив в мою директиву и прокручивать, когда я уже обращался к нему через мой контроллер?

контроллер

appControllers.controller('dummyCtrl', function ($scope, $http) {
   $http.get('locations/locations.json').success(function(data) {
      $scope.locations = data;
   });
});

HTML

<ul class="list">
   <li ng-repeat="location in locations">
      <a href="#">{{location.id}}. {{location.name}}</a>
   </li>
</ul>
<map></map> //executes a js library

Директива (работает, когда я использую имя файла, а не location.json, так как я уже просил его один раз

.directive('map', function($http) {
   return {
     restrict: 'E',
     replace: true,
     template: '<div></div>',
     link: function(scope, element, attrs) {

$http.get('locations/locations.json').success(function(data) {
   angular.forEach(data.locations, function(location, key){
     //do something
   });
});
4b9b3361

Ответ 1

Если вы хотите следовать всем "лучшим практикам", я бы рекомендовал несколько вещей, некоторые из которых затронуты другими ответами и комментариями к этому вопросу.


Во-первых, хотя это не слишком сильно влияет на конкретный заданный вами вопрос, вы указали эффективность, и лучший способ обработки общих данных в вашем приложении состоит в том, чтобы включить его в службу.

Я лично рекомендовал бы использовать AngularJS система обещаний, что сделает ваши асинхронные службы более сложными по сравнению с необработанными обратными вызовами. К счастью, служба Angular $http уже использует их под капотом. Здесь служба, которая вернет обещание, которое решает данные из файла JSON; вызов службы более одного раза не вызовет второй HTTP-запрос.

app.factory('locations', function($http) {
  var promise = null;

  return function() {
    if (promise) {
      // If we've already asked for this data once,
      // return the promise that already exists.
      return promise;
    } else {
      promise = $http.get('locations/locations.json');
      return promise;
    }
  };
});

Что касается получения данных в вашей директиве, важно помнить, что директивы предназначены для абстрактного общего манипулирования DOM; вы не должны вводить им услуги, специфичные для приложения. В этом случае было бы заманчиво просто вставлять службу locations в директиву, но это связывает директиву с этой службой.

Краткая информация о модульности кода: функции директив должны почти никогда не отвечать за получение или форматирование собственных данных. Theres ничего не мешает вам использовать службу $http из директивы, но это почти всегда неправильно. Написание контроллера для использования $http - это правильный способ сделать это. Директива уже касается элемента DOM, который является очень сложным объектом и его трудно вырезать для тестирования. Добавление сетевого ввода-вывода в микс делает ваш код намного сложнее для понимания и гораздо труднее проверить. Кроме того, сетевые блокировки ввода-вывода блокируются так, что ваша директива будет получать свои данные - возможно, в другом месте, которое вы хотите, чтобы эта директива принимала данные из сокета или принимала предварительно загруженные данные. Ваша директива должна либо принимать данные как атрибут через область действия. $Eval и/или иметь контроллер для обработки и хранения данных.

- Руководство по написанию директив AngularJS 80/20

В этом конкретном случае вы должны поместить соответствующие данные в область вашего контроллера и поделиться им с директивой через атрибут.

app.controller('SomeController', function($scope, locations) {
  locations().success(function(data) {
    $scope.locations = data;
  });
});
<ul class="list">
   <li ng-repeat="location in locations">
      <a href="#">{{location.id}}. {{location.name}}</a>
   </li>
</ul>
<map locations='locations'></map>
app.directive('map', function() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div></div>',
    scope: {
      // creates a scope variable in your directive
      // called `locations` bound to whatever was passed
      // in via the `locations` attribute in the DOM
      locations: '=locations'
    },
    link: function(scope, element, attrs) {
      scope.$watch('locations', function(locations) {
        angular.forEach(locations, function(location, key) {
          // do something
        });
      });
    }
  };
});

Таким образом, директива map может использоваться с любым набором данных о местоположении - директива не жестко закодирована для использования определенного набора данных, а просто связывание директивы путем включения ее в DOM будет не запускать случайные HTTP-запросы.

Ответ 2

Как вы говорите, вам не нужно дважды запрашивать файл. Передайте его с вашего контроллера на вашу директиву. Предполагая, что вы используете директиву внутри области действия контроллера:

.controller('MyController', ['$scope', '$http', function($scope, $http) {
  $http.get('locations/locations.json').success(function(data) {
      $scope.locations = data;
  });
}

Затем в вашем HTML (где вы вызываете директиву).
Примечание: locations является ссылкой на ваши контроллеры $scope.locations.

<div my-directive location-data="locations"></div>

И, наконец, в вашей директиве

...
scope: {
  locationData: '=locationData'
},
controller: ['$scope', function($scope){
  // And here you can access your data
  $scope.locationData
}]
...

Это всего лишь схема, указывающая на то, что вы в правильном направлении, поэтому она неполна и не проверена.

Ответ 3

Что вам нужно, это сервис:

.factory('DataLayer', ['$http',

    function($http) {

        var factory = {};
        var locations;

        factory.getLocations = function(success) {
            if(locations){
                success(locations);
                return;
            }
            $http.get('locations/locations.json').success(function(data) {
                locations = data;
                success(locations);
            });
        };

        return factory;
    }
]);

locations будет кэшироваться в службе, которая работает как модель singleton. Это правильный способ получения данных.

Используйте эту службу DataLayer в вашем контроллере, и директива соответствует следующим образом:

appControllers.controller('dummyCtrl', function ($scope, DataLayer) {
    DataLayer.getLocations(function(data){
        $scope.locations = data;
    });
});

.directive('map', function(DataLayer) {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function(scope, element, attrs) {

            DataLayer.getLocations(function(data) {
                angular.forEach(data, function(location, key){
                    //do something
                });
            });
        }
    };
});