2

I'm new to AngularJS and am still trying to wrap my head around using services to pull data into my application.

I am looking for a way to cache the result of a $http.get() which will be a JSON array. In this case, it is a static list of events:

[{ id: 1, name: "First Event"}, { id: 2, name: "Second Event"},...]

I have a service that I am trying to use to cache these results:

appServices.service("eventListService", function($http) {
    var eventListCache;

    this.get = function (ignoreCache) {

        if (ignoreCache || !eventListCache) {
            eventListCache = $http.get("/events.json", {cache: true});
        }

        return eventListCache;
    }


});

Now from what I can understand I am returning a "promise" from the $http.get function, which in my controller I add in a success callback:

appControllers.controller("EventListCtrl", ["$scope", "eventListService", 
    function ($scope, eventListService) {
        eventListService.get().success(function (data) { $scope.events = data; });
    } 
]);

This is working fine for me. What I'd like to do is add an event to the eventListService to pull out a specific event object from eventListCache.

appServices.service("eventListService", function($http) {
    var eventListCache;

    this.get = function (ignoreCache) { ... }

    //added
    this.getEvent = function (id) {
         //TODO: add some sort of call to this.get() in order to make sure the 
         //eventListCache is there... stumped
    }        
});

I do not know if this is the best way to approach caching or if this is a stupid thing to do, but I am trying to get a single object from an array that may or may not be cached. OR maybe I'm supposed to call the original event and pull the object out of the resulting array in the controller.

1
  • I typically have the service request its data upon initialization and just store (cache) it. The controller can then do $watch on the data returned from the service, and put in the scope as appropriate. Commented Apr 7, 2014 at 22:45

2 Answers 2

3

You're on the right track. Services in Angularjs are singeltons, so using it to cache your $http request is fine. If you want to expose several functions in your service I would do something like this. I used the $q promise/deferred service implementation in Angularjs to handle the asynchronus http request.

appServices.service("eventListService", function($http, $q) {
    var eventListCache;

    var get = function (callback) {
        $http({method: "GET", url: "/events.json"}).
            success(function(data, status) {
                eventListCache = data;
                return callback(eventListCache);
            }).
        }
    }

    return {
        getEventList : function(callback) {
            if(eventListCache.length > 0) {
                 return callback(eventListCache);
            } else {
                var deferred = $q.defer();
                get(function(data) {
                    deferred.resolve(data);
                }
                deferred.promise.then(function(res) {
                    return callback(res);
                });
            }
        },
        getSpecificEvent: function(id, callback) {
            // Same as in getEventList(), but with a filter or sorting of the array
            // ...
            // return callback(....);
        }
    }
});

Now, in your controller, all you have to do is this;

appControllers.controller("EventListCtrl", ["$scope", "eventListService", 
    function ($scope, eventListService) {

         // First time your controller runs, it will send http-request, second time it 
         // will use the cached variable
         eventListService.getEventList(function(eventlist) {
             $scope.myEventList = eventlist;
         });

         eventListService.getSpecificEvent($scope.someEventID, function(event) {
             // This one is cached, and fetched from local variable in service
             $scope.mySpecificEvent = event;
         });
    } 
 ]);
Sign up to request clarification or add additional context in comments.

Comments

1

You are on the right track. Here's a little help:

appServices.service("eventListService", function($http, $q) {
    var eventListCache = [];

    function getList(forceReload) {
        var defObj = $q.defer(), listHolder;

        if (eventListCache.length || forceReload) {
            listHolder= $http.get("/events.json", {cache: true});
            listHolder.then(function(data){
                eventListCache = data;
                defObj.resolve(eventListCache);
            });
        } else {
             defObj.resolve(eventListCache);
        }

        return defObj.promise;
    }

    function getDetails(eventId){
        var defObj = $q.defer();

        if(eventId === undefined){
            throw new Error('Event Id is Required.');
        }

        if(eventListCache.length === 0){
            defObj.reject('No Events Loaded.');
        } else {
            defObj.resolve(eventListCache[eventId]);
        }

        return defObj.promise;
    }

    return {
        eventList:getList,
        eventDetails:getDetails
    };
});

Then, in your controller, you handle it like this:

appControllers.controller("EventListCtrl", ["$scope", "eventListService", function ($scope, eventListService) { var eventList = eventListService.getList(); eventList.then(function(data){ $scope.events = data; });

    $scope.getEventsList = function(reloadList){
        eventList = eventListService.getList(reloadList);
        eventList.then(function(data){
            $scope.events = data;
        });
    };

    $scope.getEventDetails = function(eventID){
        var detailsPromise = eventListService.getDetails(eventID);
        detailsPromise.then(function(data){
            $scope.eventDetails = data;
        }, function(reason){
           window.alert(reason);
        });
    }
} 

]);

This way, your events are loaded when the controller first loads, and then you have the option to request a new list by simply passing in a boolean. Getting event details is also handled by an internal promise to give you some error handling without throwing a disruptive error.

Comments

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.