Reputation: 11
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
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
Reputation: 4649
My solution is to use two pipes:
async
pipe) or to the signalSignal 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