I have a scenario that seems like it should be straight forward, but I just can’t get it working.
I have an app that relies on a query param in the application route, which determines which data to load throughout. Let’s call it “team”. When the app first loads, I want to check if a team query param exists. If it does, I want to save it to local storage, if not, I want to check local storage and see if they have a team id stored there, and if so, transition to it.
However, I can’t find a way to access the query params on initial load. There is no transition object passed to setupController in the application route that I can take them from. If I put the logic in a function in the application controller and then call it in setupController, this.team is still undefined when it is called.
I can get it to work by using run.later to run the setup after 1000ms, but this is far from idea. The other option is an observer, but that’s not best practice these days. Anyone got any suggestions?
Hey @normanhaze,
Query Params in EmberJs are quirky until you understand them. In your case, I suggest you declare the team query parameter in both your src/controllers/application.js
and src/routes/application.js
:
# src/controllers/application.js
export default class ApplicationController extends Controller {
queryParams = ['team'];
team = '';
}
# src/routes/application.js
export default class ApplicationRoute extends Route {
model(params) {
// put a console.debug in here to see what I mean
console.debug('In Application Model Hook', params);
// `params.team` can be placed into local storage here
}
queryParams = {
team: { refreshModel: false } // or set to true if you want the query param changing to trigger the model hook
};
}
The queryParams
declaration in your CONTROLLER tells your application to assign the team
query parameter from the URL to the application controller’s team
variable.
The queryParams
declaration in your ROUTE determines whether to re-fire the model
-hook in the event that the user mangles/edits the team
parameter in the URL’s query-section. The model(params)
hook is where your magic can happen; you can check your local storage for a team value and compare it to the one passed in the query string.
I think this solves your problem? Let me know!
1 Like
Hi @Dan_Nelson,
Thanks for your response! I had it in my head that the local storage logic needed to live in the application controller, as this is where my team selection logic is, but you made me realise that it can in fact live in the route. I had already set refreshModel to true, and therefore the model would always fire when someone selected a new team, letting me store their chosen team every time it changes.
The model itself didn’t seem quite the right place to put this logic, as it would include a transition to the stored “team” query param (which is required by the sub-route models, but not the application model itself). However, afterModel did provide a transition object (unlike setupController) and so I was able to put the local storage logic in there:
model() {
return this.store.findAll('team');
}
afterModel(model, transition) {
const { team } = transition.to.queryParams;
const allTeamSlugs = model.map(t => t.slug);
if (team && allTeamSlugs.includes(team)) {
this.storeTeamLocally(team);
} else {
const storedTeam = this.getTeamFromLocalStore();
if (storedTeam) {
this.transitionTo({ queryParams: { team: storedTeam } })
}
}
}
I don’t actually need the team param to load the model (as the model is a list of available teams), however, I did need the model to validate the team param provided, which meant I needed to use afterModel rather than beforeModel.
I’m not sure I explained my problem very well, but thank you for helping me work through it!
1 Like
Hey @normanhaze! Glad to hear that helped. Route classes are very powerful but sometimes get overlooked. If you’re in a Controller
class and struggling to achieve something, it’s likely because you need to do that something in the Route
class. Happy coding!