Belkacz
Belkacz

Reputation: 11

How to create Custom Pipe reacting on Lang change like Translate Pipe? Some sort of Impiure resticted pipe?

I'm trying to modify a custom pipe to react when language changes. here is my custom pipe, which validate and transform date into correct format

import { ChangeDetectorRef, Pipe, PipeTransform } from '@angular/core';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { DateTime } from 'luxon';
import { FormattingService } from '../formatting.service';

@Pipe({
    name: 'datex',
    pure: false
})

export class DatexPipe implements PipeTransform {
    translatePipe: TranslatePipe;
    constructor(private translate: TranslateService, private _ref: ChangeDetectorRef) {
        this.translatePipe = new TranslatePipe(translate, _ref);
    }
    transform(value: DateTime, format: string = FormattingService.DATETIME_FORMAT): string {
        if (!value || !value.isValid) {
            return this.translatePipe.transform('N/A');
        } else if (!DateTime.isDateTime(value)) {
            // eslint-disable-next-line no-console
            console.error(new TypeError('Provided value is of type: ' + typeof(value) + '. Required type: DateTime'));
            return '';
        }
        if (value.year < 1970) {
            value = value.plus({ years: 100 });
        }
        return value.toFormat(format);
    }
}

in line

return this.translatePipe.transform('N/A');

is translation N/A into different languages, but it works only when the page is reloaded or language was set before page loaded. So i changed pipe into impure

pure: false

but with this option pipe reacts to any change detection and pipe is called way too many times. The goal I want to achieve is dynamic translation similar to Translate Pipe which one has settings pure: false, but called only when language is changed, not all of change detection. Some type of Impure restricted pipe.

Upvotes: 1

Views: 1640

Answers (2)

Pavlo Kozachuk
Pavlo Kozachuk

Reputation: 283

Create custom pipe returning Observable<string> and wrap result with async pipe in template.

import { Pipe, PipeTransform, inject } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';

@Pipe({
  name: 'translateObs',
  standalone: true
})

export class TranslateObservablePipe implements PipeTransform {
  private translateService = inject(TranslateService);
  transform(key: string | undefined): Observable<string> {
    const translationObs =
      !!key && (this.translateService.stream(key) as Observable<string>);

    return !!translationObs ? translationObs : of('');
  }
}

in template

{{ 'My awesome text' | translateObs | async }}

Upvotes: 0

rcomblen
rcomblen

Reputation: 4649

My solution is to use two pipes:

  • the translate pipe itself is pure, but returns an Observable (or even better a Signal, available starting with Angular 16)
  • a pipe to subscribe to the Observable (that would be the async pipe) or to the signal

Signal implementation (in this case using i18next):

A service to store switch language, having a signal on the current language.

@Injectable({
  providedIn: 'root',
})
export class LanguageService {
  currentLang$$: SettableSignal<string> = signal('en');

  changeLang(lang: string) {
    i18n.changeLanguage(lang).then(() => {
      this.currentLang$$.set(lang);
    });
  }
}

A pure pipe looking up the translation

@Pipe({
  name: 'i18n',
})
export class I18nPipe implements PipeTransform {
  constructor(private languageService: LanguageService) {}

  transform(value: I18NPath, options?: any): Signal<string> {
    return computed(() => {
      this.languageService.currentLang$$();// reading forces evaluation when changed
      return i18n.t('app:' + value, options) as any;
    });
  }
}

An impure pipe to read the value of the signal

@Pipe({
  name: 'signal',
  pure: false,
})
export class SignalPipe implements PipeTransform {
  transform<T>(value: Signal<T>): T {
    return value();
  }
}

and the usage in the view

<button>{{ 'button.submit' | i18n | signal }}</button>

Upvotes: 0

Related Questions