Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

performance question: directive vs pipe #601

Open
rdukeshier opened this issue Jul 13, 2017 · 11 comments
Open

performance question: directive vs pipe #601

rdukeshier opened this issue Jul 13, 2017 · 11 comments

Comments

@rdukeshier
Copy link

I'm submitting a ... (check one with "x")

[ ] bug report => check the FAQ and search github for a similar issue or PR before submitting
[x] support request => check the FAQ and search github for a similar issue before submitting
[ ] feature request

Is there a performance advantage to using the translate directive vs the (impure) translate pipe?

@ocombe
Copy link
Member

ocombe commented Jul 13, 2017

I think that the pipe has better performance advantage, but I don't have any benchmark to support that

@roger2hk
Copy link

In angular-translate, the official documentation said using directive is better than pipe. I am not sure whether this is true in ngx-translate.

https://angular-translate.github.io/docs/#/guide/05_using-translate-directive

@marcoschaule
Copy link

This "official" documentation is for "angular-translate", the AngularJS 1.x translate library.

@PlugaruT
Copy link

so, for better performance, what is better to use? pipe or the directive?

@tasiek
Copy link

tasiek commented Mar 16, 2018

One thing I know, is that translate.pipe is an impure pipe.

Angular executes an impure pipe during every component change detection cycle. An impure pipe is called often, as often as every keystroke or mouse-move.

And it really does call it 'all the time'. The comment for that is:

// required to update the value when the promise is resolved

Got it, but seems really inefficient.

I'm not sure how is it with directive, will check that.

@bgBond
Copy link

bgBond commented Sep 4, 2018

@tasiek, any update?

@ocombe, do you know if there is a difference between using the translation in html vs using it in the component?

Thanks!

@bgBond
Copy link

bgBond commented Oct 29, 2018

Is there any update?

@morteza-gho
Copy link

morteza-gho commented Nov 5, 2018

@bgBond When use pipe transform show first empty string And when use directive, show first path to translate string. But I don't know which performance is better?!

I ask this question [here] (https://stackoverflow.com/questions/53156920/which-performance-are-better-ngx-translate-directive-or-pipe)

@albanx
Copy link

albanx commented Jan 27, 2019

Using impure pipes can lead to several performance issues, I tested it and leads to huge number calls in the component, and template rendering, specially if we have a lot of strings or change cycles in your app.

I did a similar solution with a pure pipe, with one more input in order to make it change in the language change:
{{'Hello' | _:trans.lang}}

where _ is the pipe to call and trans.lang is a public variable of the translate service. In this case the translation will trigger only if the trans.lang changes to another language.

@piotr-szybicki
Copy link

piotr-szybicki commented May 29, 2019

@albanx can you share the code how did you manage to make the pipe returning the string.

@Pipe({
  name: 'ttttranslate'
})
export class TttTranslatePipe implements PipeTransform {

  constructor(private translate: TranslateService) {}

  transform(value: string, language: string): Observable<String> {
    return this.translate.get(value);
  }
}

I did something like this but the problem is that call to translate returns Observable. That requires me to chain the | async pipe at the end of every invocation. Also, your approach requires to inject translate service into every component. Do you have any thoughts on that?

@albanx
Copy link

albanx commented May 29, 2019

@piotr-szybicki
The pipe

import { Pipe, PipeTransform } from '@angular/core';
import { TranslateService } from '@services/translate.service';

@Pipe({
  name: '_'
})
export class TranslatePipe implements PipeTransform {
  constructor(public trans: TranslateService) {}

  transform(value: string, ...args): string {
    if (args.length > 1) {
      args.shift();
    }
    return this.trans._(value, args);
  }
}

The service:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { LANGUAGES } from '@constants/languages';
import { Store } from '@ngrx/store';
import AppState from '@state/app.state';
import { environment } from 'environments/environment';

@Injectable({
  providedIn: 'root'
})
export class TranslateService {
  public lang: string;
  dictionary: any = {};
  _collector: any = {};
  deployUrl = environment.deployUrl;

  constructor(private http: HttpClient, private store: Store<AppState>) {
    this.store.select(state => state.settings).subscribe(settings => this.setLanguage(settings.language));
  }

  public _(val: string, args = []) {
    const translated = this.dictionary[val] ? this.dictionary[val] : val;
    this._collector[val] = translated;

    if (args.length > 0) {
      return this.sprintf(translated, args);
    }

    return translated;
  }

  public setLanguage(lang: string) {
    if (LANGUAGES[lang]) {
      this.loadLanguage(lang, `${this.deployUrl}/${LANGUAGES[lang].path}`);
    }
  }

  private sprintf(val: string, args: Array<string>) {
    return args.reduce((p, c) => p.replace(/%s/, c), val);
  }

  private loadLanguage(lang: string, url: string) {
    console.log(JSON.stringify(this._collector));

    return this.http.get(url).subscribe(dictionary => {
      this.dictionary = dictionary;
      this.lang = lang;
    });
  }
}

A component usage (that also switch the language):

import { Component, OnInit } from '@angular/core';
import { environment } from 'environments/environment';
import { Store } from '@ngrx/store';
import AppState from '@state/app.state';
import { buildSetLanguage } from '@state/actions/settings.actions';
import { TranslateService } from '@services/translate.service';
@Component({
  selector: 'app-footer',
  templateUrl: './footer.component.html',
  styleUrls: ['./footer.component.scss']
})
export class FooterComponent implements OnInit {
  brand = environment.brand;
  version = environment.appVersion;
  constructor(private store: Store<AppState>, public trans: TranslateService) {}

  ngOnInit() {}

  setLanguage(lang) {
    this.store.dispatch(buildSetLanguage(lang));
  }
}

.html

<footer class="footer">
  <p class="footer__copyright">
    <a class="footer__link" href="https://{{ brand.url }}">© {{ brand.name }}</a> |
    <a class="footer__link" (click)="setLanguage('en')"> English </a> |
    <a class="footer__link" (click)="setLanguage('sq')"> Shqip </a>
  </p>
  <p class="termsofuse">
    <a class="footer__link" routerLink="/legal/about">{{ 'About' | _: trans.lang }} </a> |
    <a class="footer__link" routerLink="/legal/terms">{{ 'Terms of Use' | _: trans.lang }}</a> |
    <a class="footer__link" routerLink="/legal/privacy">{{ 'Privacy and GDPR' | _: trans.lang }}</a>
  </p>
  <p class="termsofuse">Version {{ version }}</p>
</footer>

Hope this helps.

The basic idea is that the language get changed/checked only when it changes, not on every change detection cycles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants