EmberJS clean store when transition on different route - ember.js

I want to clear all data from store when a user will transition
on a different route.
I want to make this clear when the transition is done on any route but
not a sub-route (I have nested-routes).
So, in event willTransition I need somehow to check if the transition
route is a sub route of the parent route. In this case, I want to keep
all data from the store, otherwise I should call this.store.unloadAll('my-object');
Thanks

Well you can get the current route name like
this.controllerFor('application').get('currentRouteName');
This return something like photos.photo. So just look if there is a . in the string and you will know its a sub route. You can also check the parent if you need that too.

You already accepted an answer but I don't think it is the correct solution (seems pretty hacky and you're having problems with it already). What you actually want to do is use the deactivate hook of your route. Please see here. The point of this hook is to execute code on change from the route. When subroutes change, the parent route is not deactivated.
deactivate: function(){
this.store.unloadAll('my-object');
}
If the object you are unloading is the same across all routes, make the deactivate hook a mixin and make all of your parent routes extend the mixin. If its a different object per route, manually write the deactivate hook for each parent route
From you comment, there is a clear async issue you have run into. You could technically do the same thing in the beforeModel hook of all of your parentRoutes. It would have the same effect, which it looks like is cache invalidation and a refetch. They're may be a more Ember Data way to do this but I wouldn't know since I don't use it.

Related

Ember: Re-render a route's template when the model changes

I've noticed that a route doesn't re-render when the model changes (i.e. transitionTo the same route with a different model). I have some jQuery plugins set up on a particular page and I need them to re-render when the model changes, so it appears as a fresh page.
Is there a way to do this? Perhaps by observing the model's ID and firing a re-render of the route somehow?
Thanks in advance
I have an ember twiddle that, I believe, does what you're looking for, but first I would like to argue there are no straightforward ways to do what you're asking because it is the opposite of what an SPA is designed to do.
Data binding (without refreshing the view) is typically a boon of an SPA, and the SPA works hard to avoid brute force reloading/refreshing/rerendering the view at all costs. It took me a while to find a solution to your question as it is stated because it seems to go against Ember design principles. Even hooks like route refresh() are meant to update the model and bound data, not reload the template.
Although other people have asked the same question before, it seems that most answers guide users towards not refreshing the whole view. More often than not, the ideal of refreshing the view is an incorrect assumption.
Following previous examples, I would suggest that your goal shouldn't be to refresh the template completely, but rather, figure out how you can make your jQuery plugin better fit in to a Single Page App/client-side JS friendly design and have it reload as a natural part of the route lifecycle.
For instance, maybe the plugin can be reloaded/reset/re-run in afterModel() or somewhere similar.
That said, I was able to accomplish what you asked for using (in my opinion, a hack) Ember.run.later() so that I could invalidate an if block and force the content to rerender. Note, this is typically not what users want since (aside from design principle reasons) it causes UI flicker.
I have a component like so.
/* will-rerender.hbs */
{{#if show}}
{{yield}}
{{/if}}
And it has a JS file like so.
/* will-rerender.js */
import Ember from 'ember';
export default Ember.Component.extend({
show: false,
didReceiveAttrs() {
this._super(...arguments);
/*
Ugly hack, but we need to reset `show` in
a separate run loop in order to force the view
to rerender.
*/
this.set('show', false);
Ember.run.later(() => {
this.set('show', true);
});
}
});
You can invoke it like this...
/* your template */
{{#will-rerender cacheKey=model.id}}
... your content to rerender ...
{{/will-rerender}}
Whenever the model.id changes the component will invoke didReceiveAttrs() causing show to invalidate and the view to refresh.
As an aside, I think the behavior of switching between models would be much more natural with {{link-to}} rather than calling transitionTo yourself.

When should I use controllers vs routes in EmberJS?

I know this question might seem a little duplicate but the other version of this question is old and some of the content (such as Views) aren't even a part of ember anymore.
I'm about 4 weeks into my internship as a front-end developer working with EmberJS. I still don't understand when it's better to use the route over the controller or vice-versa. It seems to me that every action in the route can also be used in the controller.
The one recent thing I heard was that ember routes should be stateless where as controllers should be stateful.
What is the current state of controllers and routes. When should one be used over the other?
Consider the following example to understand the state of a controller (or route, or anything), in simple terms and in current context -- lets say you have a page (like a form) with three tabs; each tab can be considered as a state - it would call different components based on the state (or the tab you are in). Now if you would happen to go back for some reason, and hit the form link again, you would see that the state would remain the same. (if you were on tab 2 when you hit back, on returning to the form, you would still be on tab 2).
So to maintain these states, controllers are the way to go, since they are singletons. Route would have lost that information, and started fresh. So basically your variables/objects in a controller would define the 'state'.
Route-actions can be as easily used as controller actions- see https://github.com/DockYard/ember-route-action-helper. So if your template for this route is just using model as the object directly, and you don't need to maintain the 'state', you can pretty much do without your controller.
But if your template was using variables which needed manipulation, you would need controller.
Hope this helps!

how to pass the Ember dynamic property from beforeModel of route to template handlebar

I want to pass a dynamic variable which is achieved from beforeModel of a route js to corresponding template's handlebar. I know that model data can be passed through model hook. However this is not model data. Also, there is no controller involved since i don't think pass variable from route to controller then template shouldn't be the easiest way.
thanks.
From ember guides beforeModel hook is appropriate for cases,
1) A decision can be made to redirect elsewhere without needing to resolve the model first.
2) Any async operations need to occur first before the model is attempted to be resolved.
so in your case, I will encourage you to do that stuff in setupController hook. if you still want to pass data from beforeModel to template then suggested solution would be, set result in route and get those values and set it in controller through setupController hook. Note: You will get default controller for every route.
This is some route data that you need in your template, that sounds like model data to me!
Also, there is always a controller involved, as one is created by the framework to be the context of the template. What this means is that the model() hook actually sets the model property of the controller, and any {{property}} you look up in your template is looked up in the related controller.
Pass the data along with the rest of the data in the model() hook.

Access the instance of the component from parent component in EmberJS

I have a "container" component
# components/container-component.js
import Ember from 'ember';
export default Ember.Component.extend({
displayComponent: 'simple-box'
});
The template is
# templates/container-component.hbs
<div>
{{component displayComponent}}
</div>
Now, from within "container-component" instance. How can I access the computed "displayComponent" instance?
Any ideas on how I could go about it?
When I use the get method I get the string identification of the component
this.get('displayComponent')
# returns (String) 'simple-box'
There is not a built-in way of retrieving children components from the parent component's js file as far as I know. If you really want to achieve this, please refer to my twiddle I have prepared for you.
As you can see from the code; within the template parent passes itself to the child and child registers itself to the parent. Note that this is an illustration of code smell and this is something I never use in my projects.
Because, in my opinion what you are trying to achieve is a clear violation of many principles that ember is built upon. Why do you want to get the child property from the parent directly? You can register actions to child component and get notified for various events within the child component. Why do you need direct access? It is better for a component to be working independently by itself. In this kind of approach you are polluting parent's code with child component's internal design considerations. You can query DOM tree via jquery within parent but getting the child component instance created for you by Ember infrastructure is a sign that you are trying to design something that is out of Ember's approach. We can help you better if you post another question (or modify this one) to describe your intention. May be someone could lead you to a better design. Best Regards.

Component to be notified on route change in EmberJS 2

My task is to develop a menu component for an Ember 2 app. This is going to be a complex component whose visual representation changes as the user goes through routes. For instance it should disable particular menu and all its items when on "/index" route but enable it and some of its items when on "/details" and so on.
So, I've got a component that is passed a singleton model (stored in a Service currently, btw, is it a right place to store globally available singleton models in Ember?). It displays the stuff well but it does not respect the current route nor catches the route changes as user goes through the app. How can I achieve it?
Summing it up:
The component needs to get current route somehow to be able to display its initial state, for instance the user bookmarked the "/details" page and visited it.
The component has to deal with route changes somehow.
Is a Service a good place to hold a singleton model (which could potentially be fetched from server).
Can you provide your thoughts on how to tackle the three above?
SOLVED: Ok, here's how it is done, thanks to #Bek's suggestions:
import Ember from "ember";
export default Ember.Component.extend({
router: Ember.inject.service("-routing"),
didInsertElement: function() {
let r = this.get("router");
console.log("Initial route", r.get("currentRouteName"));
r.addObserver("currentRouteName", this, "currentRouteNameChanged");
},
"currentRouteNameChanged": function(router, propertyName) {
console.log(router.get("currentRouteName"));
}
});
MORE QUESTIONS :) - I had to surround the currentRouteNameChanged function name with quotes (to make it a string) otherwise it was not called. I assume I miss something very basic and obvious here?
One more issue is the funky service name -routing - #Bek, any hints on how could I figure it out myself, is there a list of injectable stuff I could look up information in? It is not yet in Ember documentation I assume but where in the source code of it to check it out? How stable -routing name in general, would it become *routing or something in final version?
Answer to 1 and 2:
In latest versions of ember 2.x (in 2.2 at least) router is available as service so you can inject it to component router: Ember.inject.service('-routing') and observe changes on currentRouteName, but it is currently private service so should be used with caution as it might change (might be renamed to routing), there is also rfc https://github.com/emberjs/rfcs/pull/38 which proposes routable components which will be part of ember in the future.
Anser to 3:
Services usually stateless, but there can be exceptions and services made to share global logic/objects so it is not a bad idea