brandonhilkert
brandonhilkert

Reputation: 4475

EmberJS: Object as Query Param to Refresh Model

I followed the Query Params guide (http://guides.emberjs.com/v1.11.0/routing/query-params/) and it worked great. Specifically, refreshing the model did exactly what I wanted.

I'm moving the filter to the json-api spec and filtering takes place in a filter object. So rather than:

http://localhost:3000/accounts?id=1

The server responds to:

http://localhost:3000/accounts?filter[id]=1

I tried to get the query params to work refreshing the model based on an object, but it doesn't seem to update.

// app/controllers/accounts/index.js

import Ember from 'ember';

export default Ember.Controller.extend({
  queryParams: ['filter', 'sort'],

  filter: {},
  sort: '-id'
});


// app/routes/accounts/index.js
import Ember from 'ember';

export default Ember.Route.extend({
  queryParams: {
    filter: { refreshModel: true },
    sort: { refreshModel: true }
  },
  model: function(params) {
    return this.store.find('account', params);
  },
});

// template
<th>{{input type="text" placeholder="ID" value=filter.id}}</th>

Is it possible to have query params work with an object?

Upvotes: 4

Views: 1543

Answers (2)

Sarus
Sarus

Reputation: 3313

I know this is a bit late but you can use this workaround to allow for objects with query params. It's pretty easy to get working and so far I haven't found any issues with it.

I ran into the same problem when building an Ember app on top of my JSON API (http://jsonapi.org/).

The JSON API specification provides recommended syntax for both paging and filtering that requires object based query params.

For paging it suggests syntax like this:

/books?page[size]=100&page[number]=1

and for filtering it suggest syntax like this:

/books?filter[author]=Bob

While Ember.js Query Params (as of Ember v2.1) do not support this out of the box it is fairly simple to get working. In your controller you should map a controller property to the query param "object" as a string.

So for example, in the above "filter" example you would map a controller property called filterByAuthorValue to the query param filter[author].

The code to do this would look like this:

export default Ember.Controller.extend({
  queryParams: ['sort',{
    filterByAuthorValue: 'filter[author]'
  }],
  sort: '',
  filterByAuthorValue: ''
});

Note in the example above I also have a query param called sort (which also follows JSON API recommendations but doesn't require an object). For more information on mapping a controller property to a query param see this section of the official Ember.js guide:

http://guides.emberjs.com/v2.1.0/routing/query-params/#toc_map-a-controller-s-property-to-a-different-query-param-key

Once you have the query param created you then need to handle the query param in your router. First, the router should force the model to be refreshed when the controller property filterByAuthor changes:

export default Ember.Route.extend({
  queryParams: {
    sort: {
      refreshModel: true
    },
    filterByAuthor:{
      refreshModel: true
    }
  }
});

Finally, you now need to translate the controller property filterByAuthor into an actual object when you load the model in the router's model method and assign the value from the controller property filterByAuthor. The full router code would then look like:

export default Ember.Route.extend({
  queryParams: {
    sort: {
      refreshModel: true
    },
    filterByAuthor:{
      refreshModel: true
    }
  },
  model: function(params){
    // The params value for filtering the entity name
    if(params.filterByAuthor){
      params.filter = {};
      params.filter['author'] = params.filterByAuthor;
      delete params.filterByAuthor;
    }
    return this.store.query('book', params);
  },
});

Settings things up like this allows for an object based query param to be used with Ember and thus follow the JSON API recommendations.

The above has been tested with the following versions:

Ember             : 2.1.0
Ember Data        : 2.1.0
jQuery            : 1.11.3

Upvotes: 1

barelyknown
barelyknown

Reputation: 5560

This answer is as of Ember version 1.13.0-beta.1+canary.

The short answer: No. Query params will not work with an object.

The long answer:

As of now, a private function named _serializeQueryParams in the Router serializes the queryParams.

_serializeQueryParams(targetRouteName, queryParams) {
  var groupedByUrlKey = {};

  forEachQueryParam(this, targetRouteName, queryParams, function(key, value, qp) {
    var urlKey = qp.urlKey;
    if (!groupedByUrlKey[urlKey]) {
      groupedByUrlKey[urlKey] = [];
    }
    groupedByUrlKey[urlKey].push({
      qp: qp,
      value: value
    });
    delete queryParams[key];
  });

  for (var key in groupedByUrlKey) {
    var qps = groupedByUrlKey[key];
    var qp = qps[0].qp;
    queryParams[qp.urlKey] = qp.route.serializeQueryParam(qps[0].value, qp.urlKey, qp.type);
  }
},

qp.urlKey would evaluate to 'filter' in your example, and object would be serialized as 'object [Object]'. Even though you could override the serializeQueryParam method in your route, that wouldn't help because the queryParam key would still be 'filter', and you'd need it to be 'filter%5Bid%5D'

Based on this comment in the Ember Discussion Forum, it sounds like object query params are unlikely, and you'd be better off just flattening and unflattening the filtered fields.

Upvotes: 4

Related Questions