web-dev-qa-db-ja.com

Angularでは、JSONオブジェクト/配列をディレクティブに渡す方法は?

現在、私のアプリにはJSONファイルを取得し、「ng-repeat」を使用してそれらを反復処理するコントローラーがあります。これはすべてうまく機能していますが、同じJSONファイルを反復処理する必要があるディレクティブもあります。 1つのページで同じJSONファイルを2回要求することはできないため、これは問題を引き起こしています(また、効率が悪いため、要求しません)。 JSONファイルの1つのファイル名を変更すると、ディレクティブとコントローラーの両方が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
   });
});
52
Omegalen

すべての「ベストプラクティス」に従う場合は、いくつかの推奨事項がありますが、そのいくつかは、この質問に対する他の回答やコメントで触れられています。


まず、あなたが尋ねた特定の質問にはあまり影響を与えませんが、効率について言及しました。アプリケーションで共有データを処理する最良の方法は、それをサービスに含めることです。

個人的には、AngularJSの promise system を採用することをお勧めします。これにより、生のコールバックと比較して非同期サービスがより構成可能になります。幸いなことに、Angularの$httpサービスは既にそれらを内部で使用しています。 JSONファイルのデータに解決されるプロミスを返すサービスは次のとおりです。サービスを複数回呼び出しても、2番目の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操作を抽象化するように設計されていることを覚えておくことが重要です。アプリケーション固有のサービスをnot注入する必要があります。この場合、ディレクティブにlocationsサービスを単純​​に挿入したいのですが、これはディレクティブをそのサービスに結合します。

コードのモジュール性についての簡単な説明:ディレクティブの関数は、独自のデータを取得またはフォーマットする責任をほとんど負いません。ディレクティブ内から$ httpサービスを使用するのを止めるものは何もありませんが、これはほとんどの場合間違ったことです。 $ httpを使用するコントローラーを作成するのが正しい方法です。ディレクティブはすでに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ディレクティブは、anyの位置データのセットで使用できます。ディレクティブは、特定のデータセット、およびディレクティブをDOMに含めることによって単にリンクするだけでは、ランダムなHTTPリクエストは発生しません。

91
Michelle Tilley

あなたが言うように、ファイルを2回要求する必要はありません。コントローラーからディレクティブに渡します。コントローラーのスコープ内でディレクティブを使用すると仮定します。

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

これは正しい方向を示すための単なるアウトラインであるため、不完全であり、テストされていません。

11
Index

必要なのは適切なサービスです:

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