6

I am trying to build a factory to act as a staging area for my database models, as well as an api to perform basic CRUD calls. I want to be able to access data by storing it in a service or a factory, and keep api methods along with it so I can perform actions like these in the controller.

$scope.folders =  Folders.data(); // for a factory
$scope.folders = Folders.data; // for a Service

Folders.create({name: "My Stuff, $oid: { 5fwewe033333 }, user_id: CurrentUser.id"});

Currently I am using the Folder factory like this in the controller.

Folders.foldersData().success( function(data, status) {
   $scope.folder = data;
})
.error( function(data,status) {
   Flash.warning("There was a problem fetching your data");
});

I know I can just have a promise resolved in the controller, but with the size of the project I'm working on, I like the idea of accessing the Folders model in a service, with out having to make a server call to sync the data every time I make a change.

angular.module('cmsApp')
  .factory('Folders', function($http, $q){

    var folders = {};
    var messageWarn = "Upload Retrival Failed.";


    return {
      get: function(){
        var defered = $q.defer();

        $http.get('/folders').success( function ( data, status ) {
          defered.resolve(data);
        })
        .error( function ( data, status ) {
          defered.reject();
          Flash.warning(message_warn);
        });
        defered.promise.then( function (promise)
          folders = promise;
        });
      }, 
      data: function (){
      return folders;
    },
  }               
});

My problem is that I can't keep the folders object to persist after I call Folders.get(). It always comes back after I call Folders.data() as an empty object.

Is there a way to keep this data stored in the Factory as a up-to-date representation of the Folder model that is not dependent on hitting the server every time?

Running angular 1.2.3, on a Rails 4 API.

1
  • Yes, I'll make an example. I think the main thing you're confused about is that a factory creates a service and then is done. You can't access factory, so to speak. Only services. Commented Jan 28, 2014 at 20:38

2 Answers 2

10

You can store the promise in the service as an object on the service. I forked the expanded demo above to demonstrate http://plnkr.co/edit/2HqQAiD33myyfVP4DWg3?p=preview

As with the previous examples, the http call is only made once but this time the promise is added to the folders item on the service object which gets created by the factory.


app.factory('myService', function($http, $q) {

  return {

    myObject: '',

    get: function() {
      // Create the deffered object
      var deferred = $q.defer();

      if(!this.myObject) {
        // Request has not been made, so make it
        $http.get('my-file.json').then(function(resp) {
          console.log('Making the call!');
          deferred.resolve(resp.data);
        });
        // Add the promise to myObject
        this.myObject = deferred.promise;
      }
      // Return the myObject stored on the service
      return this.myObject;

    }

  };

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

5 Comments

I added an additional plunkr demonstrating a more usable approach that enables you to re-use the request code but still only makes the request once per controller. See plnkr.co/edit/2HqQAiD33myyfVP4DWg3?p=preview Personally, I encapsulate the makeRequest object as it's own service with more flexibility (such as HTTP GET, POST, DELETE, etc.) and inject that service into all other services that will be hitting my api.
I don't see how this really differs from my answer. There are plenty of ways to present the same concept. Further, you're using the promise anti-pattern by manually creating a promise rather than using the promise returned by the $http call.
@aaronroberson Your links are dead ? :)
@AndersMetnik The only real difference between my answer and this one is this one uses the promise anti-pattern (which is bad), and I fetch the data sooner (minor detail - easy to change). I'd be glad to help clarify what about my code you don't understand. Just let me know.
@ All. I've done some research and it seems that this pattern indeed is faulty. I wish I could retract my upvote. If anyone sees this, please use answer from @m59 instead. It is more simple, and it is more reliable.
2

In this example, the service essentially IS the data. The first time the service is injected, a promise is created and the call goes out. The service is actually the promise of that data, but when the call comes back, the promise is resolved with the data. So, the second, third, etc time the service is injected, the call isn't made again - the factory has already done its job and returned the service (the promise in this example).

Live demo (click).

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

app.controller('myCtrl', function($scope, myService) {
  myService.then(function(data) {
    $scope.data = data
  })
});

app.factory('myService', function($http, $q) {
  //this runs the first time the service is injected
  //this creates the service

  var promise = $http.get('my-file.json').then(function(resp) {
    return resp.data;
  });

  return promise;

});

Here's an expanded demo in which I use the data twice. Note the console log - the call is only ever made once. In this demo, I made the service into an object with a get method that returns the promise. I just wanted to demonstrate another way this technique could be implemented.

app.factory('myService', function($http, $q) {

  console.log('Making the call!');
  var promise = $http.get('my-file.json').then(function(resp) {
    return resp.data;
  });

  var myService = {
    get: function() {
      return promise;
    }
  };

  return myService;
});

5 Comments

Thanks @m59, But what if I wanted to store the resolved promise IN the service and be able to manipulate the data without having to hit the server again? So I could make calls like Folders.data[1] = { folder_name: "New Folder" }
I have the same question I want to store the resolved promise in the service as well
@allencoded I'm not sure what problems you're having. I demonstrated two ways that this can be done and they work fine.
They save the returned promise but I would like to save the data itself.
@allencoded Effectively, it's the same thing. What you're talking about can't work out semantically. Sure, you could apply the data directly to the service when the call comes back, but that doesn't give you any control over how the data is used. Your consumers of that data would end up trying to use the data before the ajax call returns. When accessing data from an asynchronous source, it is proper to return the promise of it. If the data is already there, that just means the then function will fire immediately.

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.