# Setting query params programmatically

**URL:** <https://discuss.emberjs.com/t/setting-query-params-programmatically/14665>\
**Category:** General\
**Created:** [April 30, 2018, 1:24am UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665 "2018-04-30T01:24:15Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![WMattGardner](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/wmattgardner/32/13891_2.png) [@WMattGardner](https://discuss.emberjs.com/u/WMattGardner)\
**Post date:** [April 30, 2018, 1:24am UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/1 "2018-04-30T01:24:15Z")

</div>

I’m interested in ways of thinking about this problem: [setting query params programatically](https://stackoverflow.com/questions/26458405/ember-programatically-set-queryparams-in-mixin). Suppose I have some content coming from a model (layers on a map, for example), and I want those layers to be “toggleable”, off or on, and preserve that state in the URL. I can try this, but it’s either ignored or since the controller is initialized before my data comes down, it won’t update:

```auto
// routes/application.js
afterModel() {
  const paramsFromModel = stuff; // munge and extract what should be param-able
  this.controllerFor('application').set('queryParams', paramsFromModel);
  this.controllerFor('application').setProperties( { /* set their detaults */ } );
}

```

Over the years, I’ve run into this problem many times, in which I’ve wanted to set up what the query params are from data that comes in from the model hook of the route. I have tried dozens of different approaches, including:

- Using an application instance-initializer, which gets me access to the store and the needed controller so that I can set up those queryParams in time. The problem is that these initializers aren’t able to be async, as deferReadiness is no longer available in these kinds of initializers (it’s only available in initializers, which won’t let me access the store as it doesn’t exist at that point in booting up the app).
- Using beforeModel will honor changes made to controller queryParams, but doing anything asynchronously (like fetching data to define those) means they aren’t set “in time”, and those QPs aren’t bound correctly.
- Using setupController (which nominally seems like the place to do this but is not) simply does not honor newly set queryParams.

Workarounds I’ve tried include:

- Using an array query param to include a list of properties that are implicitly “true”. This approach lends itself to lots of aliasing properties and general confusion around when and where query params are handled. It also requires setting up separate computed properties to manage status from the original query params.

Ember Parachute helps with a lot of these things, and also includes hooks for managing when query params are changed, but it doesn’t get what I need with the kinds of apps I’m building.

I simply cannot find a decent pattern around this, short of a computed string array on the controller (`visibleLayers: []`) which implicitly determines visibility state of layers that come in from the server. This mostly works, but it’s very confusing and doesn’t get me all the default behavior from normal QPs (working defaults).

Curious to see how 1) others have solved this issue 2) what other ways to address this issue (models that are “QP-able”, and setting up the app to honor those).

EDIT: Ideally, I would love to be able to do something like this:

```javascript
  setupController(controller, model) {
    const queryParams = model.layerGroups
      .reduce((acc, curr) => {
        acc[curr.get('id')] = curr.get('visible');
        return acc;
      }, {});

    const layerGroupIDs = model.layerGroups.mapBy('id');
    controller.set('queryParams', layerGroupIDs);

    controller.setProperties({
      ...queryParams,
    });

    this.replaceWith(this.routeName, {
      queryParams,
    });

    super.setupController(controller, model);
  }

```

But updates to the controller properties never get saved in the URL.

---

<div class="post-metadata">

**Author:** ![ef4](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/ef4/32/13470_2.png) [@ef4](https://discuss.emberjs.com/u/ef4)\
**Post date:** [April 30, 2018, 4:38am UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/2 "2018-04-30T04:38:02Z")

</div>

The difficulty here is that the query params for the current route are _inputs_ to the route hooks (including even `beforeModel`), because they can say `this.paramsFor(this.routeName)` and expect to see the query params in there. So the route hooks are necessarily too late to influence the set of possible query params.

I agree that this makes dynamically altering the set of params very awkward. It’s equivalent to dynamically altering anything else about the set of possible routes.

One solution you may not have considered is extending whichever Location implementation you’re using. That gives you total control over the boundary between Ember and the real browser URL. You can use it to implement an entirely separate strategy for adding query parameters to the URL and reading them out again.

---

<div class="post-metadata">

**Author:** ![nik](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/nik/32/14116_2.png) [@nik](https://discuss.emberjs.com/u/nik)\
**Post date:** [April 30, 2018, 9:53am UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/3 "2018-04-30T09:53:26Z")

</div>

Agree with @ef4 to think about _entirely separate strategy_ if you’re flexible in terms of building URL in any way you want. For example, consider using a location hash and parse it for params doing server requests with them. You’ll be able to manipulate browser URL string within the route with browser history preserved.

---

<div class="post-metadata">

**Author:** ![WMattGardner](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/wmattgardner/32/13891_2.png) [@WMattGardner](https://discuss.emberjs.com/u/WMattGardner)\
**Post date:** [April 30, 2018, 3:24pm UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/4 "2018-04-30T15:24:43Z")

</div>

Thank you, @nik @ef4, I feel a bit saner now.

> One solution you may not have considered is extending whichever Location implementation you’re using.

I’m intrigued by the idea of extending the HistoryLocation object, and I’m curious what that might look like so that I can get started on actually solving this issue.

This seems like something I would love to dive into but need just a few hints on how to start. Would I extend it in an initializer and add logic there? How should I access the instance? Do you know of any project-specific repositories or addons that extend the HistoryLocation API to manipulate as needed? Any links would be greatly appreciated.

> For example, consider using a location hash and parse it for params doing server requests with them.

When you say location _hash_, do you mean specifically the HashLocation API (as opposed to the HistoryLocation API), or just xLocation API in general?

EDIT:

I found this: [https://github.com/emberjs/ember.js/blob/v3.0.0/packages/ember-routing/lib/location/api.js#L41](https://github.com/emberjs/ember.js/blob/v3.0.0/packages/ember-routing/lib/location/api.js#L41).

---

<div class="post-metadata">

**Author:** ![ef4](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/ef4/32/13470_2.png) [@ef4](https://discuss.emberjs.com/u/ef4)\
**Post date:** [April 30, 2018, 4:35pm UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/5 "2018-04-30T16:35:09Z")

</div>

Yeah, start by making `app/locations/custom.js` like this:

```auto
import HistoryLocation from '@ember/routing/history-location';

export default HistoryLocation.extend({
  getURL() {
    let url = this._super();
    console.log(`I customized getURL: ${url}`);
    return url;
  }
});

```

And turn it on in `config/environment.js` by setting `locationType: 'custom'`

---

<div class="post-metadata">

**Author:** ![rwjblue](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/rwjblue/32/9411_2.png) [@rwjblue](https://discuss.emberjs.com/u/rwjblue)\
**Post date:** [April 30, 2018, 5:17pm UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/6 "2018-04-30T17:17:54Z")

</div>

Just to note, if you extend history location and still want ember-cli to work properly (e.g. navigate to a sub-url and hit refresh: you should get served the index.html at that URL instead of a 404) you will need to set `historySupportMiddleware` to `true` as well as `locationType` in your `config/environment.js`.

---

<div class="post-metadata">

**Author:** ![WMattGardner](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/wmattgardner/32/13891_2.png) [@WMattGardner](https://discuss.emberjs.com/u/WMattGardner)\
**Post date:** [April 30, 2018, 7:36pm UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/7 "2018-04-30T19:36:33Z")

</div>

Thanks, folks. `historySupportMiddleware` saved me from some future grief.

Here’s where I’ve landed: I’ve set up the behavior I want in a _service_ in this gist: [Aliased & observed properties from a collection of models · GitHub](https://gist.github.com/allthesignals/059b0fe3da6dd94b322a3a60d33cea50). The service gets me an observer that fires events when my aliased QPs change.

However, I’m not sure _where_ I should start interacting with the Location API. I was going to go down this path: move all that service logic into the custom Location object. Does that mean I should use the container lookup in the route (perhaps in the `afterModel` hook), and lookup my Location API?. This could set up some dynamicQueryParams, handle all the aliasing, etc. From there, I get access to all the Location methods I’d need for this: setURL, formatURL, etc.

🦆

---

<div class="post-metadata">

**Author:** ![WMattGardner](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/wmattgardner/32/13891_2.png) [@WMattGardner](https://discuss.emberjs.com/u/WMattGardner)\
**Post date:** [May 1, 2018, 1:52am UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/8 "2018-05-01T01:52:17Z")

</div>

Sorry for the double post, but I found an [old example (of Discourse!)](https://github.com/discourse/discourse/blob/fc65bc28bedbb7fc6dbd093c3be292e616b429f6/app/assets/javascripts/discourse/routes/discourse_location.js) of overriding HistoryLocation and managing QPs (copied to my own gist):

> <https://gist.github.com/allthesignals/6e27bf6e1e5b687b35d4e8b8e18f1dfd>

Here they are managing parsing in and out of search strings (query params) to objects, setting up [computed](https://github.com/discourse/discourse/blob/fc65bc28bedbb7fc6dbd093c3be292e616b429f6/app/assets/javascripts/discourse/routes/discourse_location.js#L200-L205)s to `replaceState`, and so on. I’ll keep going down this path with this code as an example.

I guess out of curiosity I thought there might be a better API for pushing new params that (de)serializes them, determines if they’re duplicated, determines if they’re default, etc. One thing I’m tempted to do is to simply lookup the HistoryLocation object from my service and do everything there (since I will need computeds and other state)…

EDIT: FWIW, this is where I landed: [GitHub - NYCPlanning/labs-ember-tardy-params: Bind on-the-fly query parameters to your models](https://github.com/NYCPlanning/ember-tardy-params).

---

<div class="post-metadata">

**Author:** ![djwasserman](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/djwasserman/32/13872_2.png) [@djwasserman](https://discuss.emberjs.com/u/djwasserman)\
**Post date:** [May 16, 2018, 3:26am UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/9 "2018-05-16T03:26:10Z")

</div>

Somewhat related to this topic: is it possible/even advisable to attempt to deal with query params in a component. I have a lot of code that’s duplicated to manage more complex query-param driven queries, and was hoping to extract that into a separate component, but have found it to be pretty challenging.

---

<div class="post-metadata">

**Author:** ![WMattGardner](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/wmattgardner/32/13891_2.png) [@WMattGardner](https://discuss.emberjs.com/u/WMattGardner)\
**Post date:** [May 16, 2018, 1:14pm UTC](https://discuss.emberjs.com/t/setting-query-params-programmatically/14665/10 "2018-05-16T13:14:32Z")

</div>

Hmm I think it depends on what you mean by “deal with”. If I’ve needed a lot of query params and have them accessible in many components, I’d use a service: [Global Query Params for Deeply Stateful Ember Apps | by Matt Gardner | NYC Planning Digital | Medium](https://medium.com/nycplanninglabs/global-query-params-for-deeply-stateful-ember-apps-64c046b32ded)
