Alaan Alaan - 1 month ago 7
AngularJS Question

Angular - Best practice for retrieving data from a Factory method

I'm looking for some information on the best way to retrieve data from a local JSON file and handle the response. After browsing through Stack Overflow, I have some mixed thoughts as I've seen multiple ways of doing the same thing (although no explanation on why one may or may not be preferred).

Essentially, I have an Angular app that is utilising a factory to retrieve data from a JSON file; I'm then waiting for the response to resolve in my controller before using it in my html file, similar to the below:

Option 1



Factory:

comparison.factory('Info', ['$http', function($http) {
var retrievalFile = 'retrievalFile.json';

return {
retrieveInfo: function() {
return $http.get(retrievalFile);
}
}

}]);


Controller:

comparison.controller('comparisonController', ['$scope', 'Info', function($scope, Info) {

Info.retrieveInfo().then(function(response) {
$scope.info = response.data;
});

}]);


My main point of contention is figuring out when it's best to wait for the response to resolve, or if it even matters. I'm toying with the idea of having the factory return the fulfilled promise, and wait for the controller to retrieve the data also. In my view, it's best to abstract all data retrieval out of the controller and into the factory, but I'm not sure if this extends to waiting for the actual data to be returned within the factory itself. With this in mind, I'm confused about whether to opt for option 1 or option 2 and would really appreciate some feedback from more experienced/qualified developers!

Option 2



Factory:

comparison.factory('Info', ['$http', function($http) {
var retrievalFile = 'retrievalFile.json';

return {
retrieveInfo: function() {
return $http.get(retrievalFile).then(function(response) {
return response.data;
});
}
}

}]);


Controller:

comparison.controller('comparisonController', ['$scope', 'Info', function($scope, Info) {

Info.retrieveInfo().then(function(response) {
$scope.info = response;
});

}]);


Thank you for any input/suggestions in advance!

Answer

It depends on what your controller is expecting and how you set up your application. Generally, I always go with the second option. Its because I usually have global error or success handlers in all api requests and I have a shared api service. Something like below.

var app = angular.module('app', []);

app.service('ApiService', ['$http', function($http) {
    var get = function(url, params) {
    $http.get(url, { params: params })
        .then(handleSuccess, handleError);
  };

  // handle your global errors here
  // implementation will vary based upon how you handle error
  var handleError = function(response) {
    return $q.reject(response);
  };

  // handle your success here
  // you can return response.data or response based upon what you want
  var handleSuccess = function(response) {
    return response.data;
  };
}]);

app.service('InfoService', ['ApiService', function(ApiService) {
    var retrieveInfo = function() {
    return ApiService.get(retrievalFile);

    /**
    // or return custom object that your controller is expecting
    return ApiService.get.then(function(data) {
      return new Person(data);
    });
    **//
  };

  // I prefer returning public functions this way
  // as I can just scroll down to the bottom of service 
  // to see all public functions at one place rather than
  // to scroll through the large file
  return { retrieveInfo: retrieveInfo };
}]);

app.controller('InfoController', ['InfoService', function(InfoService) {
  InfoService.retrieveInfo().then(function(info) {
    $scope.info = info;
  });
}])

Or if you are using router you can resolve the data into the controller. Both ngRouter and uiRouter support resolves:

$stateProvider.state({
    name: 'info',
  url: '/info',
  controller: 'InfoController',
  template: 'some template',
  resolve: {
    // this injects a variable called info in your controller
    // with a resolved promise that you return here
    info: ['InfoService', function(InfoService) {
        return InfoService.retrieveInfo();
    }]
  }
});

// and your controller will be like
// much cleaner right
app.controller('InfoController', ['info', function(info) {
    $scope.info = info;
}]);
Comments