Recommended way to rerender page in Ember after language switch - ember.js

I need to rerender whole page application after language has switched. I don't like to use observers for it because of performance issues.
Language switch is done by a Ember.View. I tried to rerender parentView after changing Ember.I18n.translations but running into a known bug. It works one time but Ember Inspector shows parentView has dutzend of children views of it's own afterwards. After another switch parentView got destroyed. Just like demonstrated in this JSFiddle.
Here is simplified code of my view:
export default Ember.View.extend({
templateName: 'language-switch',
languageChanged: function() {
// change language
var language = this.get('controller.language.selected');
Ember.I18n.translations = translations[language];
// rerender page
this.get('parentView').rerender();
}.observes('controller.language.selected')
});
There is also a discussion about that problem on discuss.emberjs.com. I tried the suggestions there but they don't work. Last post suggest a work-a-round to map over all views, rerender them and afterwards use a transition. I don't like that one since I am afraid getting side problems by that hack.
I am pretty sure there must be a way to do a language switch in Ember with ember-i18n but what's the right way to do that?
Update:
I tried to implement the hack from discuss.emberjs.com. It's working but it's very limited. I implement it like this in the view:
var self = this;
var currentRoute = self.get('controller.currentRouteName');
this.get('controller.target').transitionTo('loading').then(function(){
self.get('controller.target').transitionTo(currentRoute);
});
Problem is that all data stored in model is lost. I did not find a way to get the current model and use it in transition. Also query parameters are lost. That limitation makes this work-a-round unacceptable for my application.
Update App.reset():
As suggested in comment I tried to use App.reset(). It works better than transitionTo work-a-round but doesn't match all needs. If calling App.reset() queryParams aren't lost but model is reseted. I'm looking for a way to rerender application while keeping current model and queryParams.
It seems that mostly these problem is handled by a full page reload on locale change. E.q. one of the main developers of ember-i18n said so in this discussion.

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.

Rerender view after change route's dynamic segment

I have a Route with dynamic segment :id. When I change only the dynamic segement part manually in browser url input field, then goes a transition, and all model hooks are called as expected.
The problem is: none of the views are rerendered. I guess it is because only model changed - not the UI. But I have some UI logic in views' didInsertElement handler - reinitialize UI plugins and so on.
How to force ember to rerender view after dynamic segment change?
I agree with the josh's comment. if you are rerendering views with the change of dynamic segment means your code is not written properly. But still if you want to go like that i am gonna give is a part of code.
In your route:
model: function(params){
model.set('id', params.id);
}
In your view which needs to be rerendered:
_modelIdChange: function(){
this.rerender();
}.observes('controller.model.id')
But i don't suggest this. I would rather prefer proper bindings. But i did use rerendering in some cases which mostly when you end up using jquery plugins.

Ember: Cannot read property 'enterStates' of undefined and textfield update

I have the following problem with ember.
I have a table with a set of datas. I have an event that returns me the current element of the table. Then it opens another view by transitioning into a new state and writes the selected table data in a textfield.
click: function(e) {
var element = $(e.target).closest("td");
App.tee = element.text().trim();
var router;
router = this.get('controller.target.router');
router.transitionTo('newRoute')
As you can see I have some other routes in my router as well.
The last two routes(home and profile) are part of a nav-tab. They work perfectly beside I click on the table. Then i get the following error when i click on a tab: Uncaught TypeError: Cannot read property 'enterStates' of undefined
Ok i give it another try to explain what i wanted to do.
What i want to do is to create a table with some data (for example persons).
When i click on a specific person in the table the corresponding table-data should be shown in the textfields that appear below. Whenever i click on another person the textfields below should change to the informations of the current clicked person in the table. When i click "New" Button a more detailed Tabview appears on the right side with some additional informations. I was playing around with ember and so far i just implemented some of the views and the routing. Im stucked as i have tried to implement the event that updates the textfield below the table. It updates once but after it has transitioned into the new state(newRoute) nothing happens. I know the code is very raw, but it is just a test to understand how this works in ember.
Ok the solution was easier than i thought. The problem was not the state changing. It was more a problem of how to access the data and how to effect the change of binded data. I realised too late that i needed to understand how the variable access works in Ember and what exactly the App.initialize() method does. So App.initialize() initializes all Controller classes in the router. If you want to access any variables within a controller you have to get the access over the router like
{{view Ember.TextField valueBinding="App.router.tableController.person"}}
Secondly i wasnt familiar with the usage of the set and get methods in Ember and the difference between extend and create. I wondered before where ember instantiates my object.
So my problem had nothing to do with states it was just a totally misunderstanding of the ember framework. Im a noob thats all.
Ok, this is the first shot of the answer.
I think the main issue is just a typo gotoHome instead of goToHome in the template.
By the way I get rid of some deprecation warnings by using <button {{action }}></button> instead of Ember.Button.
There is some other warnings when I click on the table, because you are referencing some properties which don't exist.
here is the corresponding fiddle: http://jsfiddle.net/Sly7/rKw9A/25/
Since I don't understand how it should work exactly, I'm not sure of the overall behavior. I let you explain me the flow (by editing the question please).
Any other comment is welcome :)

In an Ember view, what's the best way to run code after the rerendering of an internal child view?

I found a lot of questions about how to initialize jQuery plugins with dynamic data etc and usually the answer is that you should use the didInsertElement hook. My problem is that when using ember-data, initially the view is inserted empty and the template vars are filled afterwards through the bindings. Thus in didInsertElement the elements I need are not there.
I found a solution that works for me but I think there must be a better solution.
In addition to the didInsertElement method I also observe the attributes in the controller and call my code from there, too, wrapped in a Ember.run.next so the child view is rendered before.
Is there a better solution to react to updated child views?
It depends a bit on the size of your application, and the number of elements your view is rendering.
Edit: You might successfully observer the isLoaded property of the RecordArray. Some details here: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/record_arrays/adapter_populated_record_array.js#L21
One option that I have used for views that have a single object as their content is something like:
MyApp.CustomView = Ember.View.extend({
content: null,
contentObserver: function() {
this.rerender();
}.observes('content')
}
If your view contents is a list, it makes little sense to re render the view for each item in the list, though.
However, I think this approach is fairly similar to what you are already doing, though.
Another approach is to implement your own adapter with Ember Data. This way, you can tell the rest of your application that it's finished loading your data. Something like:
MyApp.Adapter = DS.Adapter.create({
//Finding all object of a certain type. Fetching from the server
findAll: function(store, type, ids) {
var url = type.url;
jQuery.getJSON(url, function(data) {
store.loadMany(type, data);
});
//Perform some custom logic here...
}
}
This should work, but its not as generic as it should/could be though.
Also, you might want to take a look at this commit, which allows for registering a one-time event.
https://github.com/emberjs/ember.js/commit/1809e65012b93c0a530bfcb95eec22d972069745#L0R19
I had this problem and came up with a solution: make a handlebars helper that tells you when the sub-section has rendered. Hope that helps some other folks!

rendering views dynamically on EmberJS

I just having a little trouble to implement a special kind of view for Ember, I'm digging on the source for days but can't find how to make it work... Can you take a look and tell me what's wrong? It's a small code, a specific problem when rendering one view from another (it's not doing the binds right...).
The sample code (with comments) that demonstrate the problem is here: http://jsfiddle.net/wilkerlucio/rUUuN/
Edit:
Just to clarify, I'm trying to do a view that dynamically render another view. It can be useful for a lot of implementations, like tabs for example. On tabs you have the tabs and the container that shows current tab, so, the view that I'm trying to accomplish is like this current tab container. Each tab has it own view, and I need that my view be able to render the view for the current tab.
I know I can do things like just hide a view and show the other, but it's not the way I want it right now. This CardView that I'm creating should have a binding to a property that will return a view instance, and the CardView will render this view, and will update if the property that points the view updates.
You can see a more full covered example about what I'm trying to do here: http://jsfiddle.net/wilkerlucio/Ztdpb/
Thanks
I think that you need to specify the template as such:
App.CardView = Ember.View.extend({
defaultTemplate: SC.Handlebars.compile('{{App.obj.value}}')
});
or
App.CardView = Ember.View.extend({
templateName: 'sample'
});
If you are planning to have many child views, then you may want to try to use a collection view.
This link is a bit old, but it is still not bad: http://guides.sproutcore20.com/using_handlebars.html
I've also blogged about how it implemented CRUD operations with Ember (SC2) here.
Hope this helps.