I’m interested in ways of thinking about this problem: setting query params programatically. 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:
// 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:
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.