Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - Multiple Directive Instances making XHR call multiple times

I have an Angularjs directive 'ExampleDirective' which has the controller 'ExampleController'. The controller defines two Promise objects where each Promise object makes an Http GET request and returns the response.

In the directive, we get the response data from the promise objects and process them to render the directive.

ExampleDirective gets instantiated twice within the same view and each instance makes it's own Http GET requests. This causes performance issues on the front end due to two requests sent at the same time to make expensive database calls and read from the same table as well.

Controller:

angular.module('exampleModule')
  .constant("EXAMPLE_URL", "{% url 'example:get_example' %}")
  .controller('exampleCtrl', ['$scope', '$http', 'EXAMPLE_URL', exampleCtrl]);

function exampleCtrl($scope, $http, EXAMPLE_URL) {
  $scope.examplePromise = $http.get(EXAMPLE_URL).then(function(response) {
    return response.data;
  });
}

Directive:

angular.module('exampleModule')
.directive('exampleDirective', ['exampleFactory', 'STATIC_URL',      '$http', '$window', exampleDirective]);

function exampleDirective(exampleFactory, STATIC_URL, $http, $window) {
  return {
    scope: {
      title:'@?',
      loadingImage:'@?',
    },
    restrict: 'AE',
    templateUrl: STATIC_URL + 'example/example-template.html',
    controller: "exampleCtrl",

    link: function (scope, element, attrs) {

     //add default options:
    if (!scope.title) {
      scope.title = 'Example Title';
    }
    if (!scope.loadingImage) {
      scope.loadingImage = '';
    }

    scope.examplePromise.then(function(data) {
      scope.exampleData = data;
      // do something
    });
  }
 };
}

Is there a way to instantiate a directive multiple times but not have to make the Http GET requests in the controller twice?

UPDATE This is what I did, I added a service as suggested in the answer.

Service:

angular.module('chRatingsModule')
.factory('chExampleFactory', ['$http', 'EXAMPLE_URL', chExampleFactory]);

function chExampleFactory($http, EXAMPLE_URL) {
  var api = {}
  var promise = null;
  api.examplePromise = examplePromise; 

  function examplePromise() {
    if (promise == null) {
      promise = $http.get(EXAMPLE_URL).then(function(response) {
        return response.data;
      });
    }
    return promise;
  }

  return api;
}

Updated Directive:

angular.module('exampleModule')
.directive('exampleDirective', ['exampleFactory', 'STATIC_URL',      '$http', '$window', exampleDirective]);

function exampleDirective(exampleFactory, STATIC_URL, $http, $window) {
  return {
    scope: {
      title:'@?',
      loadingImage:'@?',
    },
    restrict: 'AE',
    templateUrl: STATIC_URL + 'example/example-template.html',

    link: function (scope, element, attrs) {

    exampleFactory.examplePromise.then(function(data) {
      scope.exampleData = data;
      // do something
    });
  }
 };
}
like image 489
nael Avatar asked Mar 09 '16 20:03

nael


Video Answer


1 Answers

First solution, probably the best one: don't make the call from the directive, which should just be a graphical element. Do the call from the controller, and pass the data as argument to both directives.

Second solution, use a service in the directive, and always return the same promise:

myModule.factory('myService', function($http) {
    var promise = null;
    var getData = function() {
        if (promise == null) {
            promise = $http.get(...).then(...);
        }
        return promise;
    };

    return {
        getData: getData
    };
});
like image 187
JB Nizet Avatar answered Sep 27 '22 20:09

JB Nizet