Tricky behavior of AngularJS $resource service.

When using $resource service of AngularJS in one of the projects recently, i faced a tricky problem and thought it may be valuable to share the solution here.

 

Namely, one of the back-end services is returning an Array of String values like this, when making a GET call using a REST client:

[
  "Value_1",
  "Value_2",
  "Value_3",
  (...)
]

 

Having a standard AngularJS service defined like this:

angular.module('myAppBackendService', ['ngResource'])
  .factory('BackendApi', ['$resource', 'BackendHost', 'BackendPort', 'BackendVersion',
    function ($resource, BackendHost, BackendPort, BackendVersion) {
      var connString = BackendHost + ':' + BackendPort + '/' + BackendVersion;
      return {
        values: $resource(connString + '/values/:id',
        {
          id:'@id'
        }, {
          query: {method: 'GET', isArray: true},
          get: {method: 'GET', params:{id:'@id'}, isArray: true},
          save: {method: 'POST', isArray: true}
        })
      };
  }]);

 

and invoked like this

$scope.values = BackendApi.values.get(
  function(value) {
    // do something interesting with returned values here
    $log.debug('Success: Calling the /values back-end service', value);
  },
  function(errResponse) {
    // do something else in case of error here
    $log.debug('Error: Calling the /values back-end service', errResponse);
  });

 

i was getting a successful response from the server, however the data format which i was getting was completely unexpected to me:

[
  {
    "0" : "V",
    "1" : "a",
    "2" : "l",
    "3" : "u",
    "4" : "e",
    "5" : "_",
    "6" : "1"
  },
  {
    "0" : "V",
    "1" : "a",
    "2" : "l",
    "3" : "u",
    "4" : "e",
    "5" : "_",
    "6" : "2"
  }
]

you can imagine my surprise when trying to figure out what the heck was wrong with it?

 

After spending some time trying to google out a solution, i finally found the reason for such behavior. Listen to this:

“…ngResource expects an object or an array of objects in your response”

“…When isArray is set to true in the list of actions, the ngResource module iterates over each item received in the response and it creates a new instance of a Resource. To do this Angular performs a deep copy between the item received and the Resource class, which gives us an object with special methods ($save$deleteand so on)”

“…Internally angular uses angular.copy to perform the deep copy and this function only operates with objects andarrays, when we pass a string, it will treat it like an object.

Strings in JS can behave as arrays by providing sequential access to each character. angular.copy will produce the following when passed a string

angular.copy('hi',{}) => {0:'h', 1:'i'}

Each character becomes a value in an object, with its index set as the key. ngResource will provide a resource with properties 0 and 1.”

 

 

So, what are the possible solutions then?

  1. Use the “transformResponse” action of $resource service (you can read more about this in the documentation of the service itself, here)
  2. Use the lower level $http service:
    $http.get('/res').success(function(data){
      $scope.test = data;
    });
    
  3. Return an array of objects in your json response:
    [
      {'data': "hello"},
      {'data': "world"}
    ]
    

 

Happy coding!

 

 

 

 

Resources:

Advertisements

Tagged: ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: