Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with a REST API returning object instead of array for collection in AngularJS?

Tags:

rest

angularjs

The back-end team I am working with implemented a REST API which returns collections as object (instead of array). For example, calling GET /some_resources would return:

{
  id_1: {
          p1: "lorem",
          p2: 4 
        }, 
  id_2: {
          p1: "ipsum", 
          p2: 2
        }, 
  id_n: {
          p1: "sic", 
          p2: 7
        }
}

This structure is limiting to work with in Angular as you can't leverage the ng-repeat directive for instance (e.g orderBy and filter do not work with object).

For the previous example, ideally, I would rather expect an array of objects:

[ 
  {id: "id_1", p1: "lorem", p2: 4},
  {id: "id_2", p1: "ipsum", p2: 2},
  {id: "id_n", p1: "sic", p2: 7}
]

This would allow to directly work with the object returned by the ngResource service and sent it back without touching its structure. It would also allow to fully use the ng-repeat directive.

When I asked the back-end team to change the structure of their response of collections from object to array, they argued that the API consumer should not lead the design of the structure for the response.

I partially agree with their argument, so for now, the only solution I found consist in transforming the object into an array, work with this object into my scope and retransform the array back to the original object structure, before sending it back to the API.

This leads to a lot of code boilerplate and I am wondering if there is an "angular way" to deal with this situation.

like image 237
Eturcim Avatar asked Jun 06 '16 02:06

Eturcim


3 Answers

"they argued that the API consumer should not lead the design of the structure for the response"

Argue back - If the only consumer of the API is the front end currently, then they have time to put it in the proper format before other consumers are added. That data is perfectly suited to be an array instead of an object. I don't know how big your data set could potentially be but one thing you don't want to be doing in the future is manipulating objects - large or small - on the front end. Browsers have limited resources, servers can always have resources added.

like image 186
Brant Avatar answered Nov 01 '22 00:11

Brant


Angular JS's ng-repeat don't work well with objects, and collections should be arrays.

Your API Team is wrong, they are not building a RESTful API if they do not implement it the way the community use and create it. APIs should be built for the benefit of the consumer, it is an Interface just as like UI is built for the users.

And you are right to argue that it should be an array instead of an object. Here's one way of designing an API that is built by a community of experts: ebook

There are so many libraries that already work with RESTful APIs very well, if your backend will not implement the API correctly, you won't be able to use those great libraries created by the community. Plus, your future consumers will have a hard time using the API and will avoid using your product as a third party app

like image 3
Louie Almeda Avatar answered Oct 31 '22 23:10

Louie Almeda


Update

let resource = {
  id_1: {
          p1: "lorem",
          p2: 4 
        }, 
  id_2: {
          p1: "ipsum", 
          p2: 2
        }, 
  id_n: {
          p1: "sic", 
          p2: 7
        }
}
let model = [];

Object.keys(resource).forEach(
    key=>{
        let o = resource[key];
        o['id'] = key;
        model.push(o)
    }
);

After applying this to your resource, the output will be an array, with keys id , p1, p2 in compliance with your preferred format. Once you're done working with the model, you can get it back in its original format:

let original={};
model.forEach(o => original[o.id] = {p1:o.p1, p2:o.p2});

Original Answer

I don't know of an "Angular way". I use the code below to handle similar situations. This is typescript but could easily be adapted to javascript. It was inspired by David Sherret's code

getObjValues(e: any) {
    return Object.keys(e).map(k => e[k]);
}

let resource = {
  id_1: {
          p1: "lorem",
          p2: 4 
        }, 
  id_2: {
          p1: "ipsum", 
          p2: 2
        }, 
  id_n: {
          p1: "sic", 
          p2: 7
        }
}

let model = getObjValues(resource)

model will be an array of elements with keys p1 and p2. If you need to recover the original object you'll need more code though both to save the initial id_1, id_2 and to reconstruct the original object.

like image 1
BeetleJuice Avatar answered Oct 31 '22 23:10

BeetleJuice