0

I have a very strange problem with an angular app we are building. Whenever i load some data from a resource defined like below (simplest example I could build) I get some data back wich I can use for databinding (eg. ng-repeat='message in messages' or as {{message.id}}) however I can never read it from javascript by accessing it as an array or object (depending on wether i used get({id:myId}) or query()).

Iterating over it only gives me keys like $get, $query, $save, etc... but no actual data.

app.factory('Message', ['$resource', function($resource) {
    return $resource('MY_URL/messages/:id', {id: '@id'});
}]);

app.service('messageService', ['$rootScope', 'Message', function($rootScope, Message) {
    var messages = Message.query();
    var selectedMessage = null;

    var service = {};

    service.get = function(id) {
                    // Problem A (see below for details)
        if (arguments.length === 1) return Message.get({id: id});
        else return messages;
    };

var MenuCtrl = function($scope, Project, messageService, otherService) {
    $scope.projects = Project.query();
    $scope.messages = messageService.get();

    // Problem B (details below)
};

At Problem A i want to be able to return a single element form a collection that has already been fetched, however i need some way to handle calls that happen before the data is ready.

At problem B i would like to process some of the fetched data and pass the result to "otherService" however i need a way to delay this until the data is ready.

5
  • you didn't show how you go about accessing those resources from JavaScript so it is a bit hard to answer. How do you iterate over a retrieved resource? Commented Nov 19, 2012 at 13:22
  • The iteration was just a test with a for(each) loop. However i would expect to be able to: alert(messages[1].id) Commented Nov 19, 2012 at 13:29
  • OK, still, it would be good to see the exact code as I'm assuming that you are treating calls to Resource as synchronous calls while those are async. More info here: stackoverflow.com/questions/11966252/… Commented Nov 19, 2012 at 13:57
  • Thank you, i did not know that. I'm pretty sure that's my problem. I will do some testing and post the before and after code in case it's helpfull for other people. Commented Nov 19, 2012 at 14:20
  • Looks like duplicate of stackoverflow.com/questions/13045771 then. Commented Nov 19, 2012 at 15:34

1 Answer 1

1

I have only seen this issue come up in unit testing, and the way to get around it is by "flushing" the mock $httpBackend.

According to the API docs:

The $httpBackend used in production, always responds to requests with responses asynchronously. If we preserved this behavior in unit testing, we'd have to create async unit tests, which are hard to write, follow and maintain. At the same time the testing mock, can't respond synchronously because that would change the execution of the code under test. For this reason the mock $httpBackend has a flush() method, which allows the test to explicitly flush pending requests and thus preserving the async api of the backend, while allowing the test to execute synchronously.

Here's an example with some context:

// define a resource within a service
angular.module('app.services', ['ngResource'])
  .factory('Message', function ($resource) {
    var url = ...
      , params = ...
      , actions = ...;

    return $resource(url, params, actions);
  }

// Query the resource in a controller
function MessagesCtrl ($scope, $routeParams, Message) {
  $scope.messages = Message.query();
}

// unit test with a mocked backend
describe('MessagesCtrl', function() {
  var scope, ctrl, $httpBackend, messages;

  beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
    $httpBackend = _$httpBackend_;
    scope = $rootScope.$new();

    messages = [
      {
        id: '1',
        text: 'foo',
      }, {
        id: '2',
        text: 'foo',
      }
    ]

    $httpBackend.expectGET('/api/messages').respond(messages);
    ctrl = $controller('MessagesCtrl', {$scope: scope});
  }));

  it('should get a list of messages', function () {

    // THE NEXT LINE WILL SOLVE THE PROBLEM
    $httpBackend.flush();

    expect(scope.message).toEqualData(message);
  });
});
Sign up to request clarification or add additional context in comments.

4 Comments

Why is this voted down? If @martijnve is trying to access the results of an async call before the results are available, this is what will happen. I have seen the exact issue described in the question and this is how I solved it...
Thanks for the aswer, the link about the promises is enlightening. However i am not experiencing this problem in my unittests but in my regular controller. What happens is i load my data and immediately want do do something with the data through js. Based i your link i fugured i could call then(myfunc(data){...}) on the result of Message.get() but this gives me an error saying: "TypeError: Object #<e> has no method 'then'".
@martijnve I'd love to help you figure this out, but need to see exactly what you're trying to do. Can you post some more detailed examples with context in your question?
I have edited my question to provide a more concrete example of what i am running into.

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.