pwlarry
pwlarry

Reputation: 75

Angular service cache

I'm trying to cache response from $http into an object for a session in angular, so once the initial call has been made, every other call to service.getCategories() (e.g), will get the data from the object rather than to the api.

The service is being resolved at the route, but there is authentication, which will redirect to another route - calling service.getCategories() again.

I'm attempting this by setting an init variable on call, then all other calls will direct to the populated object - but it seems to reset the service somehow, and the returned object gets populated twice, so there's double of everything. See below:

var $ = require('jquery');

module.exports = angular.module('app.common.services.category', [])
  .factory('categoryService', ['$http', '$q', '$rootScope', function($http, $q, $rootScope) {

// API Parameters
var deferred = $q.defer();

// Services
var Categories = {

  init: false,
  categories: [],
  index: 0,

  // Retrieve all data on load.
  // Loaded into an array for all other services
  // to use after init.
  getCategories: function(page) {

    if(!Categories.init) {

      $http.get('api/core/get_category_index')
           .then(function(result) {

        var data = result.data.categories;

        $.each(data, function(i, category) {
          category.index = Categories.index;
          Categories.categories.push(category);
          Categories.index++;
        });

        Categories.init = true;

        return deferred.resolve(Categories.categories);

      });

      // Return promise once catgories is resolved
      return deferred.promise;

    } else {


      return Categories.categories;

    }

  },

  allCategories: function() {

    return Categories.categories;

  }

}

return Categories;

}]);

Upvotes: 0

Views: 89

Answers (1)

Stefan van de Vooren
Stefan van de Vooren

Reputation: 2707

A problem with your approach is when the service function getCategories is called for the second time, the first time server request may not is resolved, causing a second call to the server. So you should move the init flag directly after the function call getCategories.

An other problem is that in your case you don't know whether the function will return a promise or an Array. I Suggest always returning an Array

module.exports = angular.module('app.common.services.category', [])
.factory('categoryService', ['$http', '$q', '$rootScope', function($http, $q, $rootScope) {

// API Parameters var deferred;

// Services var Categories = {

        categories: [],
        index: 0,

        // Retrieve all data on load.
        // Loaded into an array for all other services
        // to use after init.
        getCategories: function(page) {

            if(!deferred) {

               // replacement for intit flag
                deferred = $q.defer();

                $http.get('api/core/get_category_index')
                    .then(function(result) {

                        var data = result.data.categories;

                        $.each(data, function(i, category) {
                            category.index = Categories.index;
                            Categories.categories.push(category);
                            Categories.index++;
                        });


                        deferred.resolve(Categories.categories);

                    });


            }

             // always return a promise
             return deferred.promise;



        },

        allCategories: function() {

            return Categories.categories;

        }

    }

    return Categories;

}]);

Maybe you can return the service itself with the promise. Then you could write everywhere something like:

myService.load().then(
 function success(theService) {
    theService.allCategories()
 }
);

Now it doesn't matter anymore whether the service was loaded before or not

Upvotes: 1

Related Questions