WARNING: query params are an experimental feature. You must be using a recent canary build of Ember, and enable the query-params feature flag. For more info on enabling feature flags visit the Feature Flags guide

Query Parameters Edit Page


In general, the dynamic segments of a URL are a serialized representation of a model, commonly for example the model's ID. However, sometimes you need to serialize other application state into the URL. This could be further parameters that affect the loading of the model from the server, e.g. what page of a result set you are viewing, or it could be information about client side state, e.g. sort order when the records are sorted on the client.

There can also be more global information that you want to serialize into the url, for example if you want to store an auth token in the URL, or filter all models in your application globally. It's also possible that there is a lot of parameters that you want to serialize in the url that are inconvenient to store in normal dynamic segments. This might apply when you have a map view and need to store X, Y, and zoom coordinates along with a set of visible layers on the map. Although this is possible to do with dynamic segments, it can be inconvenient. For any of these use case, you can consider using query params instead.

Specifying Query Parameters

Query params are baked right into your routes. This is essential so that the router and helpers can know what valid transitions and query parameter states are. Here is an example of defining which query params some routes respond to:

1
2
3
4
5
App.Router.map(function() {
  this.resource('posts', {queryParams: ['sort', 'direction']}, function() {
    this.resource('post', {path: "/:id", queryParams: ['showDetails']});
  });
});

Route Hooks

Query params are passed into the hooks on your routes when you have defined them. Only query params that have been defined to apply to the route in the router.map call will be passed in. If no query params are active for this route, an empty object {} is passed in.

1
2
3
4
5
6
7
App.PostsRoute = Ember.Route.extend({
  beforeModel:      function( queryParams, transition ) {},
  model:            function( params, queryParams, transition ) {},
  afterModel:       function( resolvedModel, queryParams, transition ) {},
  setupController:  function( controller, context, queryParams ) {},
  renderTemplate:   function( controller, context, queryParams ) {}
});

Only the query parameters that you specify to apply to a route are passed in, even if the parameters apply to parent routes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
App.Router.map(function() {
  this.resource('posts', {queryParams: ['sort', 'direction']}, function() {
    this.resource('post', {path: "/:id", queryParams: ['showDetails', 'sort']});
  });
});

// If the user visits the URL:
// /posts/1?sort=name&direction=asc&showDetails=yes&otherQueryParam=something

App.PostsRoute = Ember.Route.extend({
  model: function( params, queryParams, transition ) {

    // queryParams is {sort: 'name', direction: 'asc'}
    // showDetails is not passed in because it's only registered on the child route
    // otherQueryParam is not passed in because it's not registered on any route

  }
});

App.PostRoute = Ember.Route.extend({
  model: function( params, queryParams, transition ) {
    // queryParams is {sort: 'name', showDetails: 'yes'}
    // direction is not passed in because it's only registered on the parent route
    // otherQueryParam is not passed in because it's not registered on any route
  }
});


1
2
3
4
5
6
7
8
9
// IndexRoute has no query params defined

App.IndexRoute = Ember.Route.extend({
    beforeModel:      function( transition ) {},
    model:            function( params, transition ) {},
    afterModel:       function( resolvedModel, transition ) {},
    setupController:  function( controller, context ) {},
    renderTemplate:   function( controller, context ) {}
});

Transitioning Query Params

transitionTo now accepts a final argument, which is an object with the key queryParams.

1
2
3
4
5
this.transitionTo('post', object, {queryParams: {showDetails: true}});
this.transitionTo('posts', {queryParams: {sort: 'title'}});

// if you just want to transition the query parameters without changing the route
this.transitionTo({queryParams: {direction: 'asc'}});

You can also use add query params to URL transitions:

1
this.transitionTo("/posts/1?sort=date&showDetails=true");

The link-to helper supports specifying query params.

1
2
3
4
{{#link-to 'posts' direction=asc}}Sort{{/link-to}}

// Binding is also supported
{{#link-to 'posts' directionBinding=otherDirection}}Sort{{/link-to}}

The link-to helper takes into account query parameters when determining its "active" state, and will set the class appropriately. The active state is determined by working out if you clicked on the link, would the query params end up the same? You don't have to supply all of the current, active query params for this to be true.

"Stickiness"

By default, query params are "sticky". This means that if you are on a url like /posts?sort=name, and you executed transitionTo({queryParams: {direction: 'desc'}}) or clicked {{#link-to 'posts' direction=desc}}, the resulting url will be /posts?sort=name&direction=desc.

To clear query params, give a falsy value (but not undefined), e.g. transitionTo({queryParams: {direction: null}}) or {{#link-to 'posts' direction=false}}

It's also possible to clear all query params by passing false, e.g. transitionTo({queryParams: false}) or {{#link-to 'posts' queryParams=false}}

Boolean Query params

Boolean query params are serialized without the truth value, e.g. transitionTo('posts', {queryParams: {sort: true}}) would result in the url /posts?sort

This is for two reasons:

  1. passing false is the way to clear query parameters
  2. The string "false" is truthy in javascript. i.e. if ("false") { alert('oops'); } will show an alert.