Search code examples
angularangular-material2right-to-left

Angular2 material sidenav - changing rtl/ltr direction dynamically


I'm trying to change my layout dynamically according to users' language selection and switch from LTR to RTL on the fly.

I'm using Angular 2 rc6, material 2.0.0-alpha.9-3

It looks like when the page is loaded, it works perfectly with either rtl or ltr. However, when it's changed dynamically from within the app (see plunker), the direction changes but the generated element md-sidenav-content has a miscalculated margin-right and margin-left.

Digging further, I managed to see that the _dir object has an eventEmitter that should watch for on change events of _dir and recalculate the margins

@angular/material/sidenav/sidenav.js:

_dir.dirChange.subscribe(function () { return _this._validateDrawers(); });

But it's never called when changing the direction dynamically. Although, _dir holds the correct value when page is being loaded for the first time, whether its ltr or rtl.

Finally, here's a plunker to demonstrate the behaviour:

http://plnkr.co/edit/o8lXWC?p=preview

I suspect I'm not using _dir correctly but not managed to figure out what's the right way to do it.


Solution

  • Take a look at source code Dir directive

    @Input('dir') _dir: LayoutDirection = 'ltr';
    

    As you can see you're changing _dir property instead of dir setter:

    set dir(v: LayoutDirection) {
      let old = this._dir;
      this._dir = v;
      if (old != this._dir) {
        this.dirChange.emit(null);
      }
    }
    

    So i think your solution should be like:

    view

    <md-sidenav-layout fullscreen dir="ltr" #root="$implicit">
    
    <select #langSelect (change)="changeLang(langSelect.value, root)">
    

    component

    changeLang(lang, root: Dir) {
      this.translate.use(lang);
      root.dir = lang == "fr" ? "rtl" : "ltr";
    }
    

    This way you can omit direction: string property on your component.

    And one note:

    translate: TranslateService; //it's redundant `private translate: TranslateService` does it
    direction: string; // omit it
    
    constructor(private translate: TranslateService) {
      this.direction = "ltr"; // omit it
      translate.addLangs(["en", "fr"]);
      translate.setDefaultLang('en');
    
      let browserLang = translate.getBrowserLang();
      translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
      this.translate = translate; // it's redundant
    }
    

    Here's your Plunker Example

    If you think that this is the wrong decision then check this

    Hope this will help you!