在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
   });
});
3个解决方案
91 votes

如果您想遵循所有“最佳实践”,那么我建议您做一些事情,对此问题的其他答案和评论中会涉及其中的一些内容。


首先,虽然它对您所询问的特定问题影响不大,但您确实提到了效率,并且在应用程序中处理共享数据的最佳方法是将其分解为服务。

我个人建议使用AngularJS的promise系统,与原始回调相比,这将使您的异步服务更具可组合性。 幸运的是,Angular的map服务已经在后台使用它们。 这是一项服务,该服务将返回一个对JSON文件中的数据进行解析的Promise; 多次调用服务不会导致第二个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操作而设计的。 您不应向他们注入特定于应用程序的服务。 在这种情况下,很容易将map服务注入指令,但这会将指令耦合到该服务。

除了代码模块化之外,还有一点:指令的功能几乎永远不应该负责获取或格式化自己的数据。 没有什么可以阻止您从指令中使用$ http服务,但这几乎总是错误的做法。 编写使用$ http的控制器是正确的方法。 指令已经接触到DOM元素,而DOM元素是一个非常复杂的对象,很难进行测试。 将网络I / O添加到组合中会使您的代码更加难以理解,也更加难以测试。 此外,网络I / O会以指令获取数据的方式锁定-也许在其他地方,您可能希望该指令从套接字接收数据或获取预加载的数据。 您的指令应通过scope。$ 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 要求。

Michelle Tilley answered 2020-01-14T11:54:17Z
11 votes

如您所说,您不需要两次请求文件。 将其从控制器传递到指令。 假设您在控制器范围内使用指令:

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

这只是一个指引您正确方向的提纲,因此尚不完整,未经测试。

Index answered 2020-01-14T11:54:55Z
5 votes

您需要的是适当的服务:

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

DataLayer将被缓存在作为单例模型工作的服务中。 这是获取数据的正确方法。

在您的控制器中使用此服务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
                });
            });
        }
    };
});
Howard answered 2020-01-14T11:55:24Z
translate from https://stackoverflow.com:/questions/21667613/in-angular-how-to-pass-json-object-array-into-directive