繁体   English   中英

在Angular中,如何将JSON对象/数组传递给指令?

[英]In Angular, how to pass JSON object/array into directive?

目前,我的应用程序有一个控制器,它接收一个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的promise系统 ,这将使你的异步服务与原始回调相比更具组合性。 幸运的是,Angular的$http服务已经在引擎盖下使用它们。 这是一个服务,它将返回一个解析为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操作; 应该与特定的应用服务,将它们注入。 在这种情况下,简单地将locations服务注入指令是很诱人的,但这会将指令耦合到该服务。

关于代码模块化的简要说明:指令的函数几乎不应该负责获取或格式化自己的数据。 没有什么可以阻止你在指令中使用$ http服务,但这几乎总是错误的做法。 编写控制器以使用$ http是正确的方法。 指令已经触及DOM元素,这是一个非常复杂的对象,很难用于测试。 在混合中添加网络I / O会使您的代码更难以理解,而且更难以测试。 此外,网络I / O以您的指令获取其数据的方式锁定 - 可能在其他地方您希望此指令从套接字接收数据或接收预加载的数据。 您的指令应该通过作用域将数据作为属性。$ 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
}]
...

这只是一个指向正确方向的大纲,因此它不完整且未经过测试。

您需要的是正确的服务:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM