jsdatajs-data-angular

How to make find() accept an array instead of one object?


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,

Is there any way to use either find() or findAll() to


Solution

  • Everything in this answer assumes you're using the HTTP adapter.

    JSData's default expecations are the following:

    By 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:

    1. Call find or findAll method of adapter
      1. Call GET method of adapter
      2. Call HTTP method of adapter
      3. Call deserialize method on server response
    2. Pass adapter response to the afterFind or afterFindAll hook
    3. Check cacheResponse
      1. If true, pass result of afterFind or afterFindAll to DS#inject
      2. If false, pass result of afterFind or afterFindAll to DS#createInstance
    4. Return final result

    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:

    1. An HTTP response interceptor
    2. The deserialize hook
    3. The afterFind or afterFindAll hook

    In 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!