1

When a user logs into the main page of my site, I typically load quite a bit of data on the home page. much more than when they come to a specific url on the page. When they hit the ome page, that actually fullfills the data requests of much of the data that I grab individually when they hit a specific page.

I like how the $http module works with $cache and I'm wanting to use the knowledge of my home page hit to populate the cache of calls I know the individual page will make.

That is, as an example, my home page calls /rest/clients which returns all clients while individual pages call /rest/client/101. What I want to do is make it so that if /rest/clients is called first, then when /rest/client/101 is called an new fresh xhr call does not have to be made but the data can be gotten from the cache as if /rest/client/101 had already been called.

I've never done a decorator before but I'm thinking maybe a decorator on the $http service? I looked through the $http code and it seemed the cache is stored in closure to the actual http call and not exposed except on the next Get.

Has anyone done this or similar? I could not find it. Any specific pseudo coding suggestions would be very welcome.

2
  • 1
    You don't need to do anything - it's not even Angular related - if the server is setting the appropriate HTTP headers caching is at a protocol level. Just make a $http call to the resource you'll need later and let the browser figure it out Commented Jan 13, 2015 at 1:38
  • I have about 300 items in my clients call and don't want to make the 300 calls to prime the cache. I've already got all the data in my /clients call (the first one) so what I'm wanting to do is a javascript for loop through my downloaded clients array and set the cache. Since my $http calls (not trivial because I have some nesting) already use the $http module, I can not change that logic and still have it work. My code stays clean that way. Commented Jan 13, 2015 at 1:53

3 Answers 3

2

In your data service you have 2 methods, getAll and getOne.

In the service define a reference to your getAll results promise.

Then in your getOne service check to see if that promise exists and if it does use it to filter out the one item that you need to satisfy your getOne need.

module.service('dataService', function($http){
  var getAllPromise = null;

  this.getAll = function(){
    if (getAllPromise !== null){
      getAllPromise;
    }

    getAllPromise = $http.get('clients');
    return getAllPromise
  };

  this.getOne = function(id){
    if (getAllPromise !== null){
      return getAllPromise
        .then(function(allData){
          //logic here to find the one in the full result set
          return theFoundItem;
        };
    }

    return $http.get('clients/' + id);
  };
});
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks Brocco. I'm trying to optimize for the case where the getone is called and the getall never needs to be called (which is most of the time for me). that is, I want to avoid calling the server for getone, only if getall was previously called. otherwise, getone should just get the one from the server.
@PeterKellner That is exactly what my example does. If you call getOne and getAll hasn't been called then the get one http call is made. But if you call getOne after getAll has been called then getOne will use the data which has been cached (via getAllPromise) from the getAll http call.
is the getAllPromise maintained because of closure?
Yes, because it is defined in the scope of the service function it will be maintained throughout the life of the application.
1

I found the solution I asked for but implementing and making it testable is proving to be beyond my skills. I'm going to go with @brocco solution but for the permanent record I'm leaving the actual answer to what I was asking. I'm not marking this as the correct solution because @brocco solution is better for my real problem. So, thank you @brocco for the help.

You can see below what I'm basically doing is to create my own $cache with $cacheFactory. I then use the .put method of the new cache object to prime my cache. Then, subsequent calls to the client/1 url will get the cache'd record without ever having to call cache/1 in real live. The cache is loaded in the for loop from the first big call.

Thanks for everyones input on this.

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

    myApp.factory('speakersCache', function($cacheFactory) {
        return $cacheFactory('speakersCacheData');
    });


    myApp.controller('personController', ['$scope','$http','speakersCache', function ($scope,$http,speakersCache) {

        $scope.getAllSpeakers = function() {

            $http.get('speakers.json',{cache: speakersCache}).
                success(function (data, status, headers, config) {

                    debugger;
                    var i;
                    for(i=0;i<data.length;i++) {
                        var url = 'speaker/' + i;
                        speakersCache.put(url, data[i]);
                    }
                }).
                error(function (data, status, headers, config) {
                    // called asynchronously if an error occurs
                    // or server returns response with an error status.
                });
        };

        $scope.getAllSessions = function() {

            $http.get('sessions.json',{cache: speakersCache}).
                success(function (data, status, headers, config) {
                    debugger;

                }).
                error(function (data, status, headers, config) {
                    // called asynchronously if an error occurs
                    // or server returns response with an error status.
                });
        };

        $scope.getOneSpeaker = function() {
            $http.get('speaker/1',{cache: speakersCache}).
                success(function (data, status, headers, config) {
                    debugger;

                }).
                error(function (data, status, headers, config) {
                  debugger;
                });
        }

        $scope.checkit = function() {

            var x = speakersCache;
            debugger;

        };




    }]);

Comments

0

If I understand you well, I have done something similar:

I have this code:

.factory('myOwnEntity', ['$filter',
        function ($filter) {
            var myOwnList = [];
            return {
                set : function (data) {
                    myOwnList = data;
                },
                get : function () {
                    return myOwnList;
                },
                find : function (id) {
                    return $filter('filter')(myOwnList, { itemId : id }).pop();
                }
            }
        }
    ])

When I make the petition to the Web Service, I store the information like this:

$http.get(url, {
    cache : true
})
.success(function (data) {
    myOwnEntity.set(data);
    defer.resolve(data);
});
return defer.promise;

Now, the next time I need some information, I just query my entity with the find method. Hope this is what you are looking for.

4 Comments

You are on the right track for what I want but I did not make clear enough that when A user hits /client/101 for example, I don't want them to have to download all the clients ,just the one client. If they hit /clients first, then I don't want them to have to do client/101 since they already go that in the big /clients call.
You can validate, if you query your entity with the find method and find a result for the client you are looking for, then don't call /client/101.
That does make sense but then my main code is coupled (and more complex). If I can't find a way to prime the cache, I think I will do what you suggest. thanks.
If you find a better way, please post it here to have it as a reference. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.