4
votes

Short version of this question: I'm trying to re-render my ApplicationView when a specific event happens (a language change). The ApplicationView only contains a simple outlet, however on re-rendering this outlet remains empty. So, what is the correct approach to re-render an entire page?

Simplified application code (http://jsfiddle.net/6ZQh7/2/):

Ember.Handlebars.registerHelper('t', function() {
    return App.get('language') == 'en' ? 'Hi there!' : 'Hallo!';
});
App = Ember.Application.create({
    language: 'en',
    ApplicationView: Em.View.extend({
        templateName: 'application'
    }),
    TestView: Em.View.extend({
        templateName: 'test'
    }),
    ApplicationController: Em.Controller.extend(),

    Router: Em.Router.extend({
        root: Em.Route.extend({
            toggleLanguage: function(router) {
                App.set('language', App.get('language') == 'en' ? 'nl' : 'en');

                // view.parentView resolves to the ApplicationView
                router.get('applicationController.view.parentView').rerender();
            },
            index: Em.Route.extend({
                route: '/',
                connectOutlets: function(router) {
                    router.get('applicationController').connectOutlet('test')
                }
            })
        })
    })
});

Corresponding HTML:

<script type="text/x-handlebars" data-template-name="application">
    <h1>{{t whatever}}</h1>
    {{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="test">
    <h2>My Test Page</h2>
    <a href="#" {{action toggleLanguage}}>Toggle language.</a>
</script>

Doing some debugging (well, some; a few hours of it), it seems that when the re-render occurs, the ContainerView that renders the outlet doesn't have a context, which means no currentView (which is bound to this context), which means nothing is rendered in the outlet at all. Scratch that, there is a working context; if I call this.get('templateData.keywords.view.context') in ContainerView#init using the debugger, I get the applicationcontroller. Interestingly enough though, this.get('templateData.keywords.view.context.view') (which should return the view for the outlet) returns undefined, whereas `this.get('templateData.keywords.view.context').get('view') does return the view.

Context: I'm trying to write an internationalized Ember.js app, which contains a simple translation helper outputting strings in the currently set language (used in conjunction with Ember-I18n). Currently changing the language requires a reload of the page, as these language strings are unbound (and I would say rightfully so, as language changes are rare and creating bindings to every string on the page sounds like a bad idea). However, I'd like to just re-render instead of reload.

2
I don't think u need to rerender the applicaionView if you just need to apply language change...use Bindings to have the direct impact on the language w/o rerendering the applicationView - Mudassir Ali
Yeah, but as stated in my last paragraph I'd have to create bindings for every string in the page, even though they're probably never going to change, which seems like overkill (and a potential performance bottleneck). Also currently I'm kind'a just curious how to do this at all :). - Elte Hupkes
No, it's not that way, check the answer & try by urself - Mudassir Ali
Ok I guess I stated my point wrong: It's not that I'd have to create bindings manually, just that there'd have to be bindings for every perceivable string in the application (be it automatically created); which I do not want. - Elte Hupkes
I agree there may be much better approaches out there, but wanted to share the approach I used in my application. - Mudassir Ali

2 Answers

0
votes

I am kinda giving the basic approach, Create language Objects as in...

App.Languages = {};
App.availableLanguages = Em.A([App.Languages.En, App.Languages.Pirate]);

App.Languages.En = Em.Object.extend({
  languageName: "English",
  appName: "My application Name"
})

App.Languages.Pirate = Em.Object.extend({
  languageName: "Pirate",
  appName: "!!!Zzzz"
})

App.set('language', App.Languages.En);

Handlebars Helper

Em.Handlebars.registerHelper('loc', function(key){
  currentLanguage = Ember.get('language');
  value = currentLanguage.get('key');
  return Ember.String.htmlSafe(value);
})

Usage:

{{loc appName}}

Changing Language: A dropdown on the top of page as in

{{Ember.Select contentBinding=App.availableLanguages optionValuePath="content" optionLabelPath="content.languageName" selectionBinding="App.language"}}

Hence, when a language is changed, thanks to ember binding the value gets updated :)

0
votes

I solved the exact same problem by adding an observer method to the application view, which listens to changes in the language setting, and if it detects a change, rerenders the view using the rerender() method.

I have a language controller:

FLOW.languageControl = Ember.Object.create({
    dashboardLanguage:null,

    content:[
     Ember.Object.create({label: "English", value: "en"}),
     Ember.Object.create({label: "Dutch", value: "nl"}),
     Ember.Object.create({label: "Spanish", value: "sp"}),
     Ember.Object.create({label: "French", value: "fr"})],

    changeLanguage:function(){
      locale=this.get("dashboardLanguage.value");
      console.log('changing language to ',locale);

      if (locale == "nl") {Ember.STRINGS=Ember.STRINGS_NL;}
      else if (locale == "fr") {Ember.STRINGS=Ember.STRINGS_FR;}
      else if (locale == "sp") {Ember.STRINGS=Ember.STRINGS_SP;}
      else {Ember.STRINGS=Ember.STRINGS_EN;}
    }.observes('dashboardLanguage')
});

with a dropdown in a handlebars file:

<label>Dashboard language: {{view Ember.Select 
  contentBinding="FLOW.languageControl.content" 
  optionLabelPath="content.label" 
  optionValuePath="content.value" 
  selectionBinding="FLOW.languageControl.dashboardLanguage" }}
</label>

And a (simplified) Navigation view, which renders the top navigation:

FLOW.NavigationView = Em.View.extend({
  templateName: 'navigation',
  selectedBinding: 'controller.selected',

  onLanguageChange:function(){
    this.rerender();
  }.observes('FLOW.languageControl.dashboardLanguage'),
});

When the navigationView detects a language change (which was caused by the user selecting a language from the dropbox), the navigationView is rerendered.

You could do the same thing on the Application view, it is just that I only need the navigation view to be rerendered.