В 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
директива (работает, когда я использую имя файла помимо местоположения.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
});
});
3 ответов:
если вы хотите следовать всем "лучшим практикам", есть несколько вещей, которые я бы рекомендовал, некоторые из которых затронуты в других ответах и комментариях к этому вопросу.
во-первых, хотя это не слишком влияет на конкретный вопрос, который вы задали, вы упомянули эффективность, и лучший способ обработки общих данных в вашем приложении-это разложить его на службу.
Я бы лично рекомендовал обнять AngularJS обещать система, что сделает ваши асинхронные службы более составными по сравнению с необработанными обратными вызовами. К счастью, угловой
$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сервис в директиву, но это связывает директиву с этой службой.коротко о модульности кода: функции директивы почти никогда не должны отвечать за получение или форматирование своих собственных данных. Ничто не мешает вам использовать службу $http из директивы, но это почти всегда неправильно. Написание контроллера для использования $http-это правильный способ сделать это. Директива уже касается элемента DOM, который является очень сложным объектом и трудно заглушить для тестирования. Добавление сетевого ввода-вывода в микс делает ваш код гораздо более сложным для понимания и гораздо более сложным для тестирования. Кроме того, сетевой ввод-вывод блокируется таким образом, что ваша директива будет получать свои данные – возможно, в каком-то другом месте вы захотите, чтобы эта директива получала данные из сокета или принимать предварительно загруженные данные. Ваша директива должна либо принимать данные в качестве атрибута через область видимости.$eval и / или иметь контроллер для обработки получения и хранения данных.
в этом конкретном случае вы должны разместить соответствующие данные в области вашего контроллера и поделиться ими с директивой через атрибут.
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 }); }); } }; });в этом Кстати, о
как вы говорите, вам не нужно дважды просить файл. Передайте его от вашего контроллера к вашей директиве. Предполагая, что вы используете директиву внутри области контроллера:
.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 }] ...Это просто контур, чтобы указать вам в правильном направлении, так что это неполный и не проверенный.
что вам нужно, это правильно сервис:
.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; } ]);The
locationsбудет кэшироваться в сервисе, который работал как одноэлементная модель. Это правильный способ извлечения данных.используйте эту услугу
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 }); }); } }; });
Comments