Ответ 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 и/или иметь контроллер для обработки и хранения данных.
В этом конкретном случае вы должны поместить соответствующие данные в область вашего контроллера и поделиться им с директивой через атрибут.
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-запросы.