在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 

指令(当我使用除了locations.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 }); }); 

如果你想遵循所有的“最佳实践”,我会推荐几件事情,其中​​一些在这个问题的其他答案和评论中被触及。


首先,虽然它对你提出的具体问题没有太多的影响,但你确实提到了效率,处理应用程序中的共享数据的最好方法是把它分解成一个服务。

我个人会推荐拥抱AngularJS的承诺系统 ,与原始callback相比,这将使您的asynchronous服务更加可组合。 幸运的是,Angular的$http服务已经在使用它们。 这是一个服务,将返回一个承诺,parsing为来自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服务注入指令将是诱人的,但是这将指令耦合到该服务。

简单的代码模块化:指令的function应该几乎不负责获取或格式化自己的数据。 没有什么可以阻止你在指令中使用$ http服务,但这几乎总是做错了。 编写一个控制器使用$ http是正确的方法。 一个指令已经触及一个DOM元素,这是一个非常复杂的对象,很难被testing。 将networkingI / O添加到组合中会使您的代码更加难以理解,而且testing起来也更加困难。 另外,networkingI / O以指令获取数据的方式locking – 也许在其他地方,您希望使用此指令从套接字接收数据或者获取预加载的数据。 你的指令应该把数据作为一个属性通过作用域。$ 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要求。

正如你所说,你不需要两次请求文件。 从你的控制器传递给你的指令。 假设你在控制器的范围内使用指令:

 .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 }] ... 

这只是一个提示你正确的方向,所以它是不完整的,没有testing。

你需要的是一个适当的服务:

 .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将被caching在作为单例模型的服务中。 这是获取数据的正确方法。

在你的控制器和指令中使用这个服务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 }); }); } }; });