Add custom translation settings

This commit is contained in:
Sean Engelhardt 2019-02-01 09:51:18 +01:00
parent 9c6a21469b
commit 8016827242
10 changed files with 180 additions and 11 deletions

View File

@ -2,12 +2,12 @@ import { TranslateDefaultParser, TranslateStore } from '@ngx-translate/core';
import { ConfigService } from '../services/config.service'; import { ConfigService } from '../services/config.service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
interface CustomTranslation { export interface CustomTranslation {
original: string; original: string;
translation: string; translation: string;
} }
type CustomTranslations = CustomTranslation[]; export type CustomTranslations = CustomTranslation[];
/** /**
* Custom translate parser. Intercepts and use custom translations from the configservice. * Custom translate parser. Intercepts and use custom translations from the configservice.

View File

@ -69,6 +69,11 @@
<h4>{{ configItem.label | translate }}</h4> <h4>{{ configItem.label | translate }}</h4>
<editor formControlName="value" [init]="tinyMceSettings"></editor> <editor formControlName="value" [init]="tinyMceSettings"></editor>
</div> </div>
<!-- Custom Translations -->
<div *ngIf="configItem.inputType === 'translations'">
<os-custom-translation formControlName="value"></os-custom-translation>
</div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -54,7 +54,7 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit {
*/ */
@Input() @Input()
public set item(value: ViewConfig) { public set item(value: ViewConfig) {
if (value.hasConstantsInfo) { if (value && value.hasConstantsInfo) {
this.configItem = value; this.configItem = value;
if (this.form) { if (this.form) {
@ -155,10 +155,7 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit {
if (this.configItem.inputType === 'datetimepicker') { if (this.configItem.inputType === 'datetimepicker') {
value = Date.parse(value); value = Date.parse(value);
} }
// TODO: Solve this via a custom input form.
if (this.configItem.inputType === 'translations') {
value = JSON.parse(value);
}
this.debounceTimeout = null; this.debounceTimeout = null;
this.repo.update({ value: value }, this.configItem).then(() => { this.repo.update({ value: value }, this.configItem).then(() => {
this.error = null; this.error = null;
@ -215,7 +212,7 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit {
* @returns wheather it should be excluded or not * @returns wheather it should be excluded or not
*/ */
public isExcludedType(type: string): boolean { public isExcludedType(type: string): boolean {
const excluded = ['boolean', 'markupText', 'text']; const excluded = ['boolean', 'markupText', 'text', 'translations'];
return excluded.includes(type); return excluded.includes(type);
} }
} }

View File

@ -3,6 +3,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from '../../../../../e2e-imports.module'; import { E2EImportsModule } from '../../../../../e2e-imports.module';
import { ConfigListComponent } from './config-list.component'; import { ConfigListComponent } from './config-list.component';
import { ConfigFieldComponent } from '../config-field/config-field.component'; import { ConfigFieldComponent } from '../config-field/config-field.component';
import { CustomTranslationComponent } from '../custom-translation/custom-translation.component';
describe('ConfigListComponent', () => { describe('ConfigListComponent', () => {
let component: ConfigListComponent; let component: ConfigListComponent;
@ -11,7 +12,7 @@ describe('ConfigListComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule], imports: [E2EImportsModule],
declarations: [ConfigListComponent, ConfigFieldComponent] declarations: [ConfigListComponent, ConfigFieldComponent, CustomTranslationComponent]
}).compileComponents(); }).compileComponents();
})); }));

View File

@ -0,0 +1,18 @@
<!-- Add new translation button -->
<div *ngFor="let translation of translations; let i = index">
<form>
<mat-form-field>
<input matInput [value]="translation.original" (input)="onChangeOriginal($event.target.value, i)" />
</mat-form-field>
<mat-icon>arrow_forward</mat-icon>
<mat-form-field>
<input matInput [value]="translation.translation" (input)="onChangeTranslation($event.target.value, i)" />
</mat-form-field>
<button mat-icon-button>
<mat-icon (click)="onRemoveTranslation(i)">close</mat-icon>
</button>
</form>
</div>
<!-- Add new translation button -->
<button mat-button (click)="onAddNewTranslation()">{{ 'Add new custom translation' | translate }}</button>

View File

@ -0,0 +1,26 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CustomTranslationComponent } from './custom-translation.component';
import { E2EImportsModule } from 'e2e-imports.module';
describe('CustomTranslationComponent', () => {
let component: CustomTranslationComponent;
let fixture: ComponentFixture<CustomTranslationComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
declarations: [CustomTranslationComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CustomTranslationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,118 @@
import { Component, forwardRef } from '@angular/core';
import { CustomTranslation, CustomTranslations } from 'app/core/translate/translation-parser';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
/**
* Custom translations as custom form component
*
* @example:
* ```html
* <os-custom-translation formControlName="value"></os-custom-translation>
* ```
*/
@Component({
selector: 'os-custom-translation',
templateUrl: './custom-translation.component.html',
styleUrls: ['./custom-translation.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomTranslationComponent),
multi: true
}
]
})
export class CustomTranslationComponent implements ControlValueAccessor {
/**
* Holds the custom translations in a list
*/
public translations: CustomTranslations = [];
/**
* Empty constructor
*/
public constructor() {}
/**
* Helper function to determine which information to give to the parent form
*/
private propagateChange = (_: any) => {};
/**
* The value from the FormControl
*
* @param obj the value from the parent form. Type "any" is required by the interface
*/
public writeValue(obj: any): void {
if (obj) {
this.translations = obj;
}
}
/**
* Hands changes back to the parent form
*
* @param fn the function to propagate the changes
*/
public registerOnChange(fn: any): void {
this.propagateChange = fn;
}
/**
* To satisfy the interface.
*
* @param fn
*/
public registerOnTouched(fn: any): void {}
/**
* To satisfy the interface
*
* @param isDisabled
*/
public setDisabledState?(isDisabled: boolean): void {}
/**
* Detects changes to the "original" word
*
* @param value the value that was typed
* @param index the index of the change
*/
public onChangeOriginal(value: string, index: number): void {
this.translations[index].original = value;
this.propagateChange(this.translations);
}
/**
* Detects changes to the translation
* @param value the value that was typed
* @param index the index of the change
*/
public onChangeTranslation(value: string, index: number): void {
this.translations[index].translation = value;
this.propagateChange(this.translations);
}
/**
* Removes a custom translation
* @param index the translation to remove
*/
public onRemoveTranslation(index: number): void {
this.translations.splice(index, 1);
this.propagateChange(this.translations);
}
/**
* Adds a new custom translation to the list and to the server
*/
public onAddNewTranslation(): void {
const newCustomTranslation: CustomTranslation = {
original: 'New',
translation: 'New'
};
this.translations.push(newCustomTranslation);
this.propagateChange(this.translations);
}
}

View File

@ -4,9 +4,11 @@ import { SharedModule } from '../../shared/shared.module';
import { ConfigRoutingModule } from './config-routing.module'; import { ConfigRoutingModule } from './config-routing.module';
import { ConfigListComponent } from './components/config-list/config-list.component'; import { ConfigListComponent } from './components/config-list/config-list.component';
import { ConfigFieldComponent } from './components/config-field/config-field.component'; import { ConfigFieldComponent } from './components/config-field/config-field.component';
import { CustomTranslationComponent } from './components/custom-translation/custom-translation.component';
@NgModule({ @NgModule({
imports: [CommonModule, ConfigRoutingModule, SharedModule], imports: [CommonModule, ConfigRoutingModule, SharedModule],
declarations: [ConfigListComponent, ConfigFieldComponent] declarations: [ConfigListComponent, ConfigFieldComponent, CustomTranslationComponent],
entryComponents: [CustomTranslationComponent]
}) })
export class ConfigModule {} export class ConfigModule {}

View File

@ -112,7 +112,9 @@ export class ViewConfig extends BaseViewModel {
* like the type="color" input... * like the type="color" input...
*/ */
public getDebouncingTimeout(): number { public getDebouncingTimeout(): number {
if (this.inputType === 'string' || this.inputType === 'text' || this.inputType === 'markupText') { if (this.inputType === 'markupText' || this.inputType === 'translations') {
return 2500;
} else if (this.inputType === 'string' || this.inputType === 'text') {
return 1000; return 1000;
} else { } else {
return 100; return 100;