ion-back-button not changing direction when dir=rtl until re-rendered in ionic 5

Viewed 415

I'm new to ionic, and i was trying to implement the ion-back-button in my mobile-header.component, positioning is going fine, but I'm getting a weird behavior as defined in the question's title. Here's some code:

mobile-header.component.html:

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-back-button color="standard" *ngIf="isVisible" defaultHref="/"></ion-back-button>
    </ion-buttons>
    <ion-title>{{'portfolio' | translate}}</ion-title>
    <ion-buttons slot="end">
      <ion-menu-button color="standard" menu="main-menu"></ion-menu-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

First of all, i tried to manually rotating in CSS like this:

ion-back-button[dir="rtl"] {
  transform: rotate(180deg);
}

Then I followed the issue on forum.ionicframework and that led me to GitHub, and figured out it was there until ionic 4 was released, now afterwards, I knew it was actually solved, but not until the component itself is re-rendered.

EDIT:

I have noticed that changing layout direction isn't actually changing the dir attribute for <html>, so I am still getting <html dir="en"> when i inspect it.

Here's how I am changing layout direction:

First: I am injecting DOCUMENT in my .service.ts file like this:

constructor(
    @Inject(DOCUMENT) private doc: Document,
    private translateService: TranslateService
  ) {}

Second: in corresponding function I am applying an explicit doc.dir value like this:

//For English (LTR)
selectEnglish() {
  this.translateService.setDefaultLang('en');
  this.translateService.use('en');
  this.doc.dir = 'ltr';
}

//For Arabic (RTL)
selectArabic() {
  this.translateService.setDefaultLang('ar');
  this.translateService.use('ar');
  this.doc.dir = 'rtl';
}

I used this method because I thought it's of the best practices, So there might be a different method for changing layout direction to solve this problem?

Here are some demo pictures:

  • Default LTR

Default LTR

  • When RTL triggered

When RTL triggered

  • When going to home page where no "back button" there

When going to home page where no "back button" there

  • Finally returning back to any other component where the back button is supposed to show

component where the back button is supposed to show

And it works again...

Here are some dependencies in my package.json, might want to see that too:

"dependencies": {
    ...
    "@angular/cdk": "^12.2.3",
    "@angular/common": "~12.1.1",
    "@angular/compiler": "~12.1.1",
    "@angular/core": "~12.1.1",
    "@angular/platform-browser": "~12.1.1",
    "@angular/platform-browser-dynamic": "~12.1.1",
    ...
    "@ionic/angular": "^5.5.2",
    "bootstrap": "^5.1.0",
    "rxjs": "~6.6.0",
    "tslib": "^2.2.0"
 }
2 Answers

I ended up using BehaviorSubject to distribute direction value to whoever subscribe to it.

Here's how my translation.service.ts looks like now

export class TranslationService {
  direction: BehaviorSubject<string> = new BehaviorSubject<string>('ltr');

  constructor(private translateService: TranslateService) {}

  initLanguages() {
    this.translateService.setDefaultLang('en');
    this.translateService.addLangs(['en', 'ar']);
    this.translateService.use('en');
  }

  selectEnglish() {
    this.translateService.setDefaultLang('en');
    this.translateService.use('en');
    this.direction.next('ltr');
  }

  selectArabic() {
    this.translateService.setDefaultLang('ar');
    this.translateService.use('ar');
    this.direction.next('rtl');
  }
}

And inside the constructor of my app.component.ts

this.translationService.direction.subscribe((dir) => {
      this.direction = dir;
});

And of course, added the [dir] attribute combined with direction value to change accordingly in my app.component.html like this

<ion-app [dir]="direction">
  <app-menu></app-menu>
  <ion-router-outlet id="main"></ion-router-outlet>
</ion-app>

Now in my mobile-header.component.ts I also subscribed to the emitted value the same way i did in my app.component.ts like this

this.translationService.direction.subscribe((dir) => {
      this.isRTL = dir === 'rtl' ? true : false;
});

Also added a .rtl CSS class in my mobile-header.component.css like this

.rtl {
  transform: scaleX(-1);
}

Thanks Calummm for this note.

Eventually, in my mobile-header.component.html I ended up using [ngClass] on ion-back-button like this

<ion-back-button mode="md" color="standard" *ngIf="isVisible" defaultHref="/" [ngClass]="isRTL? 'rtl':''"></ion-back-button>

I was really looking for a simpler way to achieve this.

Having a look through the documentation, it appears dynamically switching site direction is only partially supported. I would guess this is having flow on effects where an element is only updating the direction on instantiation, hence only being correct when navigating away and back.

In our application, we set the direction on the root element and inherit this via a selector. In your case selecting all ion back buttons when the direction is set.

html[dir="rtl"] ion-back-button {
  transform: rotate(180deg);
}

I noticed they supply some mixins in order to do this as well as found at: https://ionicframework.com/docs/v3/theming/rtl-support/

@include rtl() {
  ion-back-button {
    transform: rotate(180deg);
  }
}

Additionally I found it better to use a negative scaleX rather than rotating as this could potentially be useful for other icons in the future, such as magnifying glass. (Being held in the left hand)

transform: scaleX(-1);
Related