0

I'm trying to grab a list of countries from a GitHub repo on the fly without storing all of the information in JS or in my own API, so I wrote a one-off service to take care of this for me and cache the results in the browser in case they're needed again. However, I've noticed that the way I've written it, the $http.get is called on load rather than upon service injection into the controller--meaning that the browser is requesting all of this country data even when it's not needed (e.g., even when a user visits the country page.

The way I've structured this, because the service simply returns a promise, I can just call countrylist.then(...) in the controller. Is there a way to keep that but still delay the request to Github until it's actually needed?

Code below:

angular.module('myModule')
    .factory('countrylist', ['$http', 'growl', '$q', function ($http, growl, $q) {

        var deferred = $q.defer();
        var flagUrl = "https://raw.githubusercontent.com/hjnilsson/country-flags/master/png250px/{}.png";

        $http.get('https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/slim-2/slim-2.json',
                {cache: true})
                .success(function (data) {
                    angular.forEach(data, function (item) {
                        item.flag = flagUrl.replace("{}", item['alpha-2'].toLowerCase());
                    });

                    deferred.resolve(data);
                }).error(function (data) {
                    growl.warn("Error loading country data");
                    deferred.reject();
                });

        return deferred.promise;

    }]);
jdotjdot
  • 16,134
  • 13
  • 66
  • 118

2 Answers2

1

Everything in the factory is run as soon as the factory is injected once into any controller, which happens when your app is bootstrapped.

The only way to do this is to return an object from your factory with a method that fetches the data.

angular.module('myModule')
.factory('countrylist', ['$http', 'growl', '$q', function ($http, growl, $q) {
  return {
    fetch: function() {
      var deferred = $q.defer();
      var flagUrl = "https://raw.githubusercontent.com/hjnilsson/country-flags/master/png250px/{}.png";

      $http.get('https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/slim-2/slim-2.json',
            {cache: true})
            .success(function (data) {
                angular.forEach(data, function (item) {
                    item.flag = flagUrl.replace("{}", item['alpha-2'].toLowerCase());
                });

                deferred.resolve(data);
            }).error(function (data) {
                growl.warn("Error loading country data");
                deferred.reject();
            });

      return deferred.promise;
    }
  };
}]);

And then in your controller you would obviously have to do

countryList.fetch().then(...)
Joe
  • 2,596
  • 2
  • 14
  • 11
1

Let it be simple. It's as simple as

  1. Return promise in your service(no need to use then in service)
  2. Use then in your controller

Demo. http://plnkr.co/edit/cbdG5p?p=preview

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

app.service('myService', function($http) {
  return {
    async: function() {
      return $http.get('test.json');  //1. this returns promise
    }
  };
});

app.controller('MainCtrl', function( myService,$scope) {
  myService.async().then(function(d) { //2. so you can use .then()
    $scope.data = d;
  });
});

This is my copy/pasted answer from Processing $http response in service

Community
  • 1
  • 1
allenhwkim
  • 27,270
  • 18
  • 89
  • 122