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,
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.findAll
, then I need to search using uglier /report/?categoryId = 22
- and change the server/report/22/myaction
Is there any way to use either find() or findAll() to
Everything in this answer assumes you're using the HTTP adapter.
JSData's default expecations are the following:
GET /<resource>/:id
returns 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>/:id
deletes a single item from your databaseDELETE /<resource>
deletes a collection of items from your databaseBy default, DS#find
does
GET /<resource>/:id
, and DS#findAll
does
GET /<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:
find
or findAll
method of adapter
GET
method of adapterHTTP
method of adapterdeserialize
method on server responseafterFind
or afterFindAll
hookcacheResponse
true
, pass result of afterFind
or afterFindAll
to DS#inject
false
, pass result of afterFind
or afterFindAll
to DS#createInstance
The 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#inject
DS#inject
requires either an object that has a field specified by the
Resource's idAttribute
option, or an array of the same.
You have three opportunities to massage the data before it gets to DS#inject
:
deserialize
hookafterFind
or afterFindAll
hookIn any one of those methods you could fix the data to be what DS#inject
expects.
If you want to be able to do Report.find(22)
then you might do:
var Report = store.defineResource({
name: 'report',
afterFind: function (Report, data, cb) {
cb(null, {
id: 22,
data: data
});
}
});
Report.find(22).then(function (report) {
report.id; // 22
report.data; // [{...}, {...}]
});
All of this assumes you really want to be able to use DS#find
, but DS#find
and DS#findAll
are 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:
var Report = store.defineResource({
name: 'report',
/* Static Class Methods */
findByCategoryId: function (id) {
// Use the adapter directly
return Report.getAdapter('http').find(Report, id).then(function (data) {
// Inject the result into the store
return Report.inject({
id: 22,
data: data
});
});
}
});
Report.findByCategoryId(22). then(function (report) {
return Report.findByCategoryId(23);
}).then(function (report) {
Report.filter(); // [{...}, {...}]
});
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!