1

I need to grab some data from my db through an API and make it accessible throughout my Angular app. I understand that Services are good for storing data to be accessed from multiple controllers. However, in the following code I end up with a new $hhtp.get() each time just to get the same data.

Service:

.factory('Playlist', ['$http', function($http) {
  var playlist = {};

  playlist.getPlaylist = function() {
   return $http.get('api/playlist.php')
   .then(function (response) {
     var data = response.data;
     return data;
    })
  }

  return playlist;
}])

Controllers:

.controller('ScheduleCtrl', ['$http', 'Playlist', function($http, Playlist) {
  var self = this;

  Playlist.getPlaylist()
  .success(function(playlist) {
    self.playlist_id = playlist.id;
    fetchItems();
  })

  var fetchScheduleItems = function() {
    return $http.get('api/schedule.php/'+self.playlist_id).then(
      function(response) {
        if (response.data === "null") {
          console.log("No items");
        } else {
          self.items = response.data;
        }
      }, function(errResponse) {
        console.error('Error while fetching schedule');
    });
  };
}])
.controller('PlaylistItemCtrl', ['$http', 'Playlist', function($http, Playlist) {
  var self = this;

  Playlist.getPlaylist()
  .success(function(playlist) {
    self.playlist_id = playlist.id;
    fetchItems();
  })

  var fetchPlaylistItems = function() {
    return $http.get('api/schedule.php/'+self.playlist_id).then(
      function(response) {
        if (response.data === "null") {
          console.log("No items");
        } else {
          self.items = response.data;
        }
      }, function(errResponse) {
        console.error('Error while fetching schedule');
    });
  };
}])

Is there a way to store the Playlist ID without pinging 'api/playlist.php' from every controller?

Update

Here's a Plunkr based on Abhi's answer: http://plnkr.co/edit/or9kc4MDC2x3GzG2dNeK?p=preview

As you can see in the console, it's still hitting the server several times. I've tried nesting CachedData.save() differently, but it doesn't seem to apply.

1
  • I have updated my answer in response to your updated question. Commented May 16, 2015 at 13:03

2 Answers 2

1

I would say store your data locally (CachedData factory - rename it to something that makes sense) and inside your getPlaylist method, before doing http call, check CachedData to see if your data is present and if not, then do the http call.

The code will be something like the below. I have just written it free-hand, so there may be some errors, but you get the picture.

.factory('Playlist', ['$http', 'CachedData', function($http, CachedData) {
 var playlist = {};

playlist.getPlaylist = function() {
  if (CachedData.data) {
     // return cached data as a resolved promise
  } else 
  return $http.get('api/playlist.php')
    .then(function (response) {
    var data = response.data;
    cachedData.save(data);
    return data;
   })
  }
  return playlist;
}])

// CachedData factory

.factory('CachedData', function() {
     var _data;
     var cachedData = {
         data: _data,
         save: function(newData) {
             _data = newData;
         }
     };
     return cachedData;
})

EDIT: Also Remove fetchPlaylistItems from the controller and put it in a factory. The controller is just a glue between your viewmodel and view. Put all your business logic, http calls in a service.

EDIT: I have setup a plunk for you here. I hope it helps.

EDIT: John, the reason you are seeing two server calls is because they are from different controllers ManiCtrl1 and MainCtrl2. By the time, the getPlaylist method from MainCtrl2 is called, the first http request didn't get the chance to finish and save the data. If you add a timeout to MainCtrl2 before calling your service method, you will see that the data is retrieved from cache. See this updated plunk for a demo. This will be more useful in an app with multiple views, where you don't want to reload data when navigating back to a view. (Ideally, depending on the type of data you are caching, you will have some expiry time after which you would want to reload your data).

Sign up to request clarification or add additional context in comments.

4 Comments

I like your solution, however I'm not able to get CachedData.save() to execute from the Playlist Factory (I did notice that "CachedData" needs to be capitalized). Any thoughts?
I just typed it on-the-fly, so there may be a few syntactical errors here. Can you share a plunker showing your attempt? Then, I will be able to see why exactly it is not working.
Thanks for your help @abhishek-jain. I've updated my question with a Plunkr modified from yours. Still having issues with CachedData.save(). It should be within the .then() of my $http.get(), correct?
Thank you Abhishek! It didn't occur to me that both calls were executing at the same time. It's functioning as expected now that I've added it to my views.
0

You could do some pre validation when calling that method.

  var playlist = {};
  playlist.playlist = [];

  playlist.getPlaylist = function () {
    if (playlist.playlist.length <= 0) { //or some lodash _.isEmpty()
      $http.get('api/playlist.php')
        .then(function (response) {
          playlist.playlist = response.data;
        })
    }
    return playlist.playlist;
  };

Hope it helps!

3 Comments

Playlist is the object that the factory returns, and not where OP is storing the result.
You are right, i used it for explaining purposes, and didn't realize that, i'll edit it
I've tried your solution, but I'm not able to update playlist.playlist with the response data. It just returns the empty array. Any thoughts?

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.