3

I'm new to angular and I'm creating a simple app. It navigates between projects, pulled in via JSON:

http://plnkr.co/edit/FTfa1rcVaf85xTu65oSR?p=preview

I am also using a factory, where I make a call such as get or getOne, so that it deals with the $http in one place, only calling it if the data hasn't already been fetched.

This all works fine when you start on the home page, but when you start on an individual project page, both get and getOne are called at the same time, pulling in duplicate data. You can test this by opening the Plunker in it's own window and going to a url such as /#/projects/1.

I know why this is happening, I just can't figure out how to stop it.

Is there a simple fix for this or am I going about it the completely wrong way?

Thanks for taking a look.

2
  • Line 48: var projects = this.get(); - don't think you need that if you've previously fetched all the projects? Commented Jan 29, 2015 at 20:03
  • Hey @Ally, that's correct if you only load from the home page, but if you start with a URL such as /project/1, both get and getOne fire at the same time and getOne has nothing to grab. Perhaps I need a way to wait for the first get call to finish? Feels like I'm overcomplicating a simple task. Commented Jan 29, 2015 at 20:23

1 Answer 1

5

Have your functions return promises via $q, instead of raw data. Then you'll be able to chain them with .then() to achieve the wait you need:

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

    // Do stuff in here
    // ...
    // and depending on the results,
    // call deferred.resolve(data) or deferred.reject(error)

    // Return the promise object
    return deferred.promise;
}

Then, in the code that calls this function, you can:

MyFactory.get().then(function (data) {
    $scope.var = data;
}

This pattern is pretty common in Angular, and works well.

Updated plunkr: here. I switched around the way you were simulating the server request lag so that I could easily resolve my deferred object in the .success() callback of $http.

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

6 Comments

Thanks so much @Nate, I played around with $q.defer earlier but had no luck! I'm still confused as to how it fixes this situation, I knew it was useful for when you have multiple functions that all have to return, but can't see how it is useful here. Does it also prevent a function being called multiple times? All the examples I've read only talk about the chaining.
No, $q does not prevent the function from being called multiple times. All this pattern here accomplishes is "wrapping" an operation that will be asynchronous in a promise so you can properly run code after the operation completes. Since the get() and getOne() functions encapsulate an async operation, it's easiest if the functions themselves return promises. A lot of Angular libraries return promises in this way.
I see. This fixes my issue but doesn't actually prevent the call being made twice. This is problematic because the factory contains a projects array that needs to be populated and kept up to date, for use in other methods (that I didn't include in the example). Perhaps this method just isn't feasible, however.
To solve that problem, you could use a locking mechanism (like setting a flag to true when the first call is made) so that any subsequent calls after the first one will be returned with the same (original) promise, or be immediately returned with the cached data. Should be pretty easy to do -- let me know if you have trouble.
Cheers for the advice. Had to do a fair bit of shuffling to get it to work, but got there in the end, with a new found understanding of defer! plnkr.co/edit/R2NrNDhz3Dn2si1UMuFf?p=preview
|

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.