Using JS-Data 2.6 in Angular, I'm trying to load an array of data using a nice clean URL, e.g. /report/22. (This format makes sense, because it's following business logic - I'm loading a report data by category id, and getting several rows back.)
However,
- when I use
find, then js-data dies with the dreaded[Error] "attrs" must be an object!because it expects 1 row, and cannot be convinced otherwise - unless I'm missing something. - when I use
findAll, then I need to search using uglier/report/?categoryId = 22- and change the server - when I use actions, then it appends action name to the URI, which I don't need, e.g.
/report/22/myaction
Is there any way to use either find() or findAll() to
- have a clean URL
- load an array of data
- preferably, not changing the server implementation
Everything in this answer assumes you're using the HTTP adapter.
JSData's default expecations are the following:
GET /<resource>/:idreturns an object, e.g.{ id: 1 }GET /<resource>returns an array of objects, e.g.[{ id: 1 }, { id: 2 }]POST /<resource> { some: 'field' }creates a single item in your database and returns the updated item, e.g.{ id: 1, some: 'field' }PUT /<resource>/:id { updated: 'field' }updates a single item in your database and returns the updated item, e.g.{ id: 1, updated: 'field' }PUT /<resource> { updated: 'field' }updates a collection of items in your database and returns the updated items, e.g.[{ id: 1, updated: 'field' }, { id: 2, updated: 'field' }]-DELETE /<resource>/:iddeletes a single item from your databaseDELETE /<resource>deletes a collection of items from your databaseBy default,
DS#finddoesGET /<resource>/:id, andDS#findAlldoesGET /<resource>.Any response from the server needs to be in the right format by the time it gets to
DS#inject.The lifecycle for these calls is the following:
findorfindAllmethod of adapterGETmethod of adapterHTTPmethod of adapterdeserializemethod on server responseafterFindorafterFindAllhookcacheResponsetrue, pass result ofafterFindorafterFindAlltoDS#injectfalse, pass result ofafterFindorafterFindAlltoDS#createInstanceThe only way to avoid an injection error is to either:
A) Not inject the adapter response into the datastore
or
B) Massage the data into the correct format before it is passed into
DS#injectDS#injectrequires either an object that has a field specified by the Resource'sidAttributeoption, or an array of the same.You have three opportunities to massage the data before it gets to
DS#inject:deserializehookafterFindorafterFindAllhookIn any one of those methods you could fix the data to be what
DS#injectexpects.If you want to be able to do
Report.find(22)then you might do:All of this assumes you really want to be able to use
DS#find, butDS#findandDS#findAllare meant to be used with RESTful resources where a Resource corresponds to a table in a database and instances of the Resource correspond to rows in the table. Generating reports is typically one of those things where you're compiling data from disparate sources, doing aggregations, etc. It's not as predictable (and hence, this question).Here's another option:
Basically, there are many ways to accomplish any particular task, each with its own set of pros/cons. JSData can only generalize to so many use-cases with its default settings.
To unlock the power of JSData and maximize your productivity, you'll want to take note of the many options and hooks in JSData that allow you to mold JSData to your liking. If you find that any particular extension or customization you've written could be generalized and benefit a lot of others who have the same use-case, let us know!
Cheers!