libs/ngx-pfe/util/i18n-from-state/directive/pfe-i18n-from-state.directive.ts
Interpolates i18n text and data from the state via jsonPath and combines them into an HTMLElement composed of spans (for easy styling).
Takes an i18nFromStateText as input. The value of this key can contain placeholders that look like this.
'I blame %jsonPath%$.variableFromTheState%jsonPath% when my code does not work'
Given that the state contains $.variableFromTheState = 'Jenkins'
The final text will look like: 'I blame Jenkins when my code does not work'
Selector | [pfeI18nFromState] |
Methods |
|
Inputs |
constructor(pfeBusinessService: PfeBusinessService, translateService: TranslateService, _renderer: Renderer2, _el: ElementRef, logger: NgxLoggerService)
|
||||||||||||||||||
Parameters :
|
pfeI18nFromState | |
Type : i18nFromStateText | undefined
|
|
Async renderText | ||||||
renderText(translatedText: string)
|
||||||
Called by PfeI18nFromStateHandler when the key is changed/translated or when one of the used state values from the expression changes.
Parameters :
Returns :
Promise<void>
|
import { NgxLoggerService } from '@allianz/ngx-logger';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { PfeBusinessService } from '@allianz/ngx-pfe';
import { Directive, ElementRef, Input, OnChanges, OnDestroy, Renderer2, SimpleChanges } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { PfeI18nFromStateHandler, REPLACEMENT_SEPARATOR } from '../pfe-i18n-from-state-handler';
/**
* Text that can contain placeholders in the form: %jsonPath%$.variableFromTheState%jsonPath%
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export type i18nFromStateText = string;
/**
*
* Interpolates i18n text and data from the state via jsonPath and combines them into an
* HTMLElement composed of spans (for easy styling).
*
* Takes an i18nFromStateText as input. The value of this key can contain placeholders that look like this.
*
* 'I blame %jsonPath%$.variableFromTheState%jsonPath% when my code does not work'
*
* Given that the state contains $.variableFromTheState = 'Jenkins'
*
* The final text will look like:
* 'I blame Jenkins when my code does not work'
*
* <div [pfeI18nFromState]="I blame %jsonPath%$["app-lang"]%jsonPath% when my code does not work"></div>
*
*
*/
@Directive({
selector: '[pfeI18nFromState]',
})
export class PfeI18nFromStateDirective implements OnDestroy, OnChanges {
@Input() pfeI18nFromState: i18nFromStateText | undefined;
private pfeI18nFromStateHandler: PfeI18nFromStateHandler;
private currentlyRendering = false;
constructor(
private pfeBusinessService: PfeBusinessService,
private translateService: TranslateService,
private _renderer: Renderer2,
private _el: ElementRef,
private logger: NgxLoggerService
) {
this.pfeI18nFromStateHandler = new PfeI18nFromStateHandler(this.pfeBusinessService, this.translateService, this.logger);
// TODO: Check if a subscription to onTranslationChange, onLangChange and onDefaultLangChange needs to be added here
// subscribe to onTranslationChange event, in case the translations change
this.translateService.onTranslationChange.subscribe(() => {
if (this.pfeI18nFromState) {
this.pfeI18nFromStateHandler.initializeAndSubscribe(this.pfeI18nFromState, (translatedText: string) =>
this.renderText(translatedText)
);
}
});
// subscribe to onLangChange event, in case the language changes
this.translateService.onLangChange.subscribe(() => {
if (this.pfeI18nFromState) {
this.pfeI18nFromStateHandler.initializeAndSubscribe(this.pfeI18nFromState, (translatedText: string) =>
this.renderText(translatedText)
);
}
});
// subscribe to onDefaultLangChange event, in case the default language changes
this.translateService.onDefaultLangChange.subscribe(() => {
if (this.pfeI18nFromState) {
this.pfeI18nFromStateHandler.initializeAndSubscribe(this.pfeI18nFromState, (translatedText: string) =>
this.renderText(translatedText)
);
}
});
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.pfeI18nFromState && this.pfeI18nFromState) {
this.pfeI18nFromStateHandler.initializeAndSubscribe(this.pfeI18nFromState, (translatedText: string) =>
this.renderText(translatedText)
);
}
}
/**
* Called by PfeI18nFromStateHandler when the key is changed/translated or when one of the used state values from
* the expression changes.
*/
async renderText(translatedText: string): Promise<void> {
// FIXME: Properly fix this!
if (this.currentlyRendering) {
return;
}
this.currentlyRendering = true;
const parentHTMLElement: HTMLElement = this._el.nativeElement;
// Remove whatever it is under the element where this directive is placed
while (parentHTMLElement.firstChild) {
parentHTMLElement.removeChild(parentHTMLElement.firstChild);
}
/*
Split the text into chunks delimited by replacementSeparator
Takes every chunk and translates it again, this is useful for when the values from the
state are values like 'MONTHLY' or 'legalTermsAccepted'.
*/
for (const textChunk of translatedText.split(REPLACEMENT_SEPARATOR)) {
// create a span for every chunk and adds the text and the class.
const translatedSpan: HTMLSpanElement = this._renderer.createElement('span');
translatedSpan.className = textChunk.startsWith('$') ? `text-fragment-from-state` : `text-fragment`;
const finalText = await this.pfeI18nFromStateHandler.getFinalText(textChunk);
this._renderer.appendChild(translatedSpan, this._renderer.createText(finalText));
this._renderer.appendChild(parentHTMLElement, translatedSpan);
}
this.currentlyRendering = false;
}
ngOnDestroy(): void {
this.pfeI18nFromStateHandler.destroy();
}
}