Skip to content

Commit

Permalink
refactor: type multi language controls (#1489)
Browse files Browse the repository at this point in the history
  • Loading branch information
derschnee68 committed Feb 29, 2024
1 parent 4e99390 commit 42d5112
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 57 deletions.
@@ -1,6 +1,7 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { ListNode } from '@dasch-swiss/dsp-js';
import { MultiLanguageFormArray } from '@dasch-swiss/vre/shared/app-string-literal';
import { ListItemService } from '../list-item/list-item.service';

@Component({
Expand All @@ -16,8 +17,7 @@ import { ListItemService } from '../list-item/list-item.service';
<dasch-swiss-multi-language-input
[placeholder]="node.labels | appStringifyStringLiteral: 'all' | appTruncate: 128"
[editable]="false"
[formGroup]="readOnlyForm"
controlName="labels"
[formArray]="labelsControl"
[validators]="[]">
</dasch-swiss-multi-language-input>
Expand Down Expand Up @@ -53,6 +53,11 @@ export class ListItemElementComponent implements OnInit, OnChanges {

readOnlyForm: FormGroup;

// TODO remove with typed forms
get labelsControl() {
return this.readOnlyForm.get('labels') as MultiLanguageFormArray;
}

constructor(
private _fb: FormBuilder,
public listItemService: ListItemService
Expand Down
@@ -1,8 +1,9 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ListInfoResponse, ListNode, ListNodeInfoResponse } from '@dasch-swiss/dsp-js';
import { ListApiService } from '@dasch-swiss/vre/shared/app-api';
import { ProjectService } from '@dasch-swiss/vre/shared/app-helper-services';
import { MultiLanguageFormArray } from '@dasch-swiss/vre/shared/app-string-literal';
import { atLeastOneStringRequired } from '../../../main/form-validators/at-least-one-string-required.validator';
import { ListItemService } from '../list-item/list-item.service';

Expand All @@ -18,8 +19,7 @@ export class ListNodeOperation {
<form [formGroup]="form" (ngSubmit)="createChildNode()" style="display: flex">
<dasch-swiss-multi-language-input
style="flex: 1"
[formGroup]="form"
controlName="labels"
[formArray]="labelsControl"
[placeholder]="placeholder"
[validators]="labelsValidators">
</dasch-swiss-multi-language-input>
Expand All @@ -41,7 +41,13 @@ export class ListItemFormComponent implements OnInit {
placeholder: string;
form: FormGroup;

// TODO remove with typed forms
get labelsControl() {
return this.form.get('labels') as MultiLanguageFormArray;
}

readonly labelsValidators = [Validators.maxLength(2000)];

constructor(
private _listApiService: ListApiService,
private _fb: FormBuilder,
Expand Down
@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MultiLanguages } from '@dasch-swiss/vre/shared/app-string-literal';
import { MultiLanguageFormArray, MultiLanguages } from '@dasch-swiss/vre/shared/app-string-literal';
import { Subscription } from 'rxjs';
import { startWith } from 'rxjs/operators';
import { atLeastOneStringRequired } from '../../../main/form-validators/at-least-one-string-required.validator';
Expand All @@ -10,15 +10,13 @@ import { atLeastOneStringRequired } from '../../../main/form-validators/at-least
template: `
<dasch-swiss-multi-language-input
placeholder="Child node label *"
[formGroup]="form"
controlName="labels"
[formArray]="labelsControl"
[validators]="labelsValidators">
</dasch-swiss-multi-language-input>
<dasch-swiss-multi-language-textarea
placeholder="Child node description"
[formGroup]="form"
controlName="comments"
[formArray]="commentsControl"
[validators]="commentsValidators">
</dasch-swiss-multi-language-textarea>
`,
Expand All @@ -37,6 +35,16 @@ export class ReusableListItemFormComponent implements OnInit, OnDestroy {
readonly labelsValidators = [Validators.required, Validators.maxLength(2000)];
readonly commentsValidators = [Validators.required, Validators.maxLength(2000)];

// TODO remove with typed forms
get labelsControl() {
return this.form.get('labels') as MultiLanguageFormArray;
}

// TODO remove with typed forms
get commentsControl() {
return this.form.get('comments') as MultiLanguageFormArray;
}

constructor(private _fb: FormBuilder) {}

ngOnInit() {
Expand Down
@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MultiLanguages } from '@dasch-swiss/vre/shared/app-string-literal';
import { MultiLanguageFormArray, MultiLanguages } from '@dasch-swiss/vre/shared/app-string-literal';
import { Subscription } from 'rxjs';
import { startWith } from 'rxjs/operators';
import { atLeastOneStringRequired } from '../../../main/form-validators/at-least-one-string-required.validator';
Expand All @@ -9,16 +9,14 @@ import { atLeastOneStringRequired } from '../../../main/form-validators/at-least
selector: 'app-reusable-list-info-form',
template: `
<dasch-swiss-multi-language-input
[formArray]="labelsControl"
placeholder="Controlled vocabulary label *"
[formGroup]="form"
controlName="labels"
data-cy="labels-input">
</dasch-swiss-multi-language-input>
<dasch-swiss-multi-language-textarea
[formArray]="commentsControl"
placeholder="Controlled vocabulary description *"
[formGroup]="form"
controlName="comments"
data-cy="comments-input">
</dasch-swiss-multi-language-textarea>
`,
Expand All @@ -34,6 +32,16 @@ export class ReusableListInfoFormComponent implements OnInit, OnDestroy {
form: FormGroup;
subscription: Subscription;

// TODO remove with typed forms
get labelsControl() {
return this.form.get('labels') as MultiLanguageFormArray;
}

// TODO remove with typed forms
get commentsControl() {
return this.form.get('comments') as MultiLanguageFormArray;
}

constructor(private _fb: FormBuilder) {}

ngOnInit() {
Expand Down
Expand Up @@ -4,6 +4,7 @@ import { ClassDefinition, PropertyDefinition } from '@dasch-swiss/dsp-js';
import { getAllEntityDefinitionsAsArray } from '@dasch-swiss/vre/shared/app-api';
import { OntologyService } from '@dasch-swiss/vre/shared/app-helper-services';
import { OntologiesSelectors } from '@dasch-swiss/vre/shared/app-state';
import { MultiLanguageFormArray } from '@dasch-swiss/vre/shared/app-string-literal';
import { Store } from '@ngxs/store';
import { Subscription } from 'rxjs';
import { existingNamesValidator } from '../../../main/directive/existing-name/existing-names.validator';
Expand All @@ -25,16 +26,14 @@ import { CustomRegex } from '../../../workspace/resource/values/custom-regex';
<dasch-swiss-multi-language-input
data-cy="label-input"
placeholder="Label *"
[formGroup]="form"
controlName="labels"
[formArray]="labelsControl"
[validators]="labelsValidators">
</dasch-swiss-multi-language-input>
<dasch-swiss-multi-language-textarea
data-cy="comment-textarea"
placeholder="Comment *"
[formGroup]="form"
controlName="comments"
[formArray]="commentsControl"
[editable]="true"
[validators]="commentsValidators">
</dasch-swiss-multi-language-textarea>
Expand All @@ -50,6 +49,15 @@ export class ResourceClassFormComponent implements OnInit, OnDestroy {
};
@Output() formValueChange = new EventEmitter<FormGroup>();

// TODO remove with typed forms
get commentsControl() {
return this.form.get('comments') as MultiLanguageFormArray;
}

get labelsControl() {
return this.form.get('labels') as MultiLanguageFormArray;
}

form: FormGroup;
existingNames: [RegExp] = [new RegExp('anEmptyRegularExpressionWasntPossible')];
ontology;
Expand Down
@@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ProjectsSelectors } from '@dasch-swiss/vre/shared/app-state';
import { MultiLanguages } from '@dasch-swiss/vre/shared/app-string-literal';
import { MultiLanguageFormArray, MultiLanguages } from '@dasch-swiss/vre/shared/app-string-literal';
import { Store } from '@ngxs/store';
import { Subscription } from 'rxjs';
import { startWith } from 'rxjs/operators';
Expand Down Expand Up @@ -38,9 +38,8 @@ import { shortcodeExistsValidator } from './shortcode-exists.validator';
<dasch-swiss-multi-language-textarea
[placeholder]="('appLabels.form.project.general.description' | translate) + '*'"
[formGroup]="form"
[formArray]="descriptionControl"
data-cy="description-input"
controlName="description"
[validators]="descriptionValidators">
</dasch-swiss-multi-language-textarea>
Expand All @@ -60,6 +59,11 @@ export class ReusableProjectFormComponent implements OnInit, OnDestroy {
};
@Output() formValueChange = new EventEmitter<FormGroup>();

// TODO remove with typed forms
get descriptionControl() {
return this.form.get('description') as MultiLanguageFormArray;
}

form: FormGroup;
shortcodePatternError = { errorKey: 'pattern', message: 'This field must contains letters from A to F and 0 to 9' };
shortCodeExistsError = { errorKey: 'shortcodeExists', message: 'This shortcode already exists' };
Expand Down
2 changes: 2 additions & 0 deletions libs/vre/shared/app-string-literal/src/index.ts
Expand Up @@ -3,3 +3,5 @@ export * from './lib/multi-language-textarea.component';
export * from './lib/multi-language-input.component';
export * from './lib/human-readable-error.pipe';
export * from './lib/multi-languages.type';
export * from './lib/multi-language-form-array.type';
export * from './lib/dash-language.type';
@@ -0,0 +1,5 @@
export type DaschLanguage = 'de' | 'fr' | 'it' | 'en' | 'rm';

export function isDaschLanguage(value: string): value is DaschLanguage {
return ['de', 'fr', 'it', 'en', 'rm'].includes(value);
}
@@ -0,0 +1,9 @@
import { FormArray, FormControl, FormGroup } from '@angular/forms';
import { DaschLanguage } from './dash-language.type';

type MultiLanguageFormControl = FormGroup<{
language: FormControl<DaschLanguage>;
value: FormControl<string>;
}>;

export type MultiLanguageFormArray = FormArray<MultiLanguageFormControl>;
@@ -1,43 +1,42 @@
import { ChangeDetectorRef, Injectable } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms';
import { FormBuilder, ValidatorFn } from '@angular/forms';
import { UserSelectors } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { DaschLanguage, isDaschLanguage } from './dash-language.type';
import { MultiLanguageFormArray } from './multi-language-form-array.type';

/** Component Provider used in combination with
* MultiLanguageInputComponent and MultiLanguageTextareaComponent.
* Those two components have the same business logic (here), but differ in their html.
*/
@Injectable()
export class MultiLanguageFormService {
readonly availableLanguages = ['de', 'fr', 'it', 'en', 'rm'];
readonly availableLanguages: DaschLanguage[] = ['de', 'fr', 'it', 'en', 'rm'];
selectedLanguageIndex: number;
formGroup: FormGroup;
controlName: string;
formArray: MultiLanguageFormArray;
validators: ValidatorFn[];

inputValue: string | null = null;

get formArray() {
return this.formGroup.controls[this.controlName] as FormArray;
get selectedFormControl() {
const index = this.formArray
.getRawValue()
.findIndex(v => v.language === this.availableLanguages[this.selectedLanguageIndex]);
return this.formArray.controls[index];
}

get selectedFormControl(): FormControl {
return (
this.formArray.controls.find(
control => control.value.language === this.availableLanguages[this.selectedLanguageIndex]
) as FormControl
) // TODO as FormControl = bad pattern, should be refactored, also form should be with Typed values
.get('value') as FormControl;
get selectedLanguageControl() {
return this.selectedFormControl.controls.value;
}

get invalidErrors() {
for (const control of this.formArray.controls) {
if (control.get('value')?.errors) {
return { language: control.value.language, error: control.get('value')?.errors };
if (control.controls.value.errors) {
return { language: control.getRawValue().language, error: control.controls.value.errors };
}
}
if (this.formArray.errors) {
return { error: this.formArray.errors };
return { error: this.formArray.errors, language: undefined };
}
return undefined;
}
Expand All @@ -48,17 +47,16 @@ export class MultiLanguageFormService {
private _cd: ChangeDetectorRef
) {}

onInit(formGroup: FormGroup, controlName: string, validators: ValidatorFn[]) {
this.formGroup = formGroup;
this.controlName = controlName;
onInit(formArray: MultiLanguageFormArray, validators: ValidatorFn[]) {
this.formArray = formArray;
this.selectedLanguageIndex = this._setupLanguageIndex();
this.validators = validators;

const newFormControl = this.formArray.controls.find(
control => control.value.language === this.availableLanguages[this.selectedLanguageIndex]
);

this.inputValue = newFormControl ? this.selectedFormControl.value : null;
this.inputValue = newFormControl ? this.selectedLanguageControl.value : null;

this.formArray.valueChanges.subscribe(array => {
if (array.length === 0) {
Expand All @@ -68,7 +66,7 @@ export class MultiLanguageFormService {
});
}

onInputChange(newText: any) {
onInputChange(newText: string) {
if (newText === '') {
if (this.inputValue && this.inputValue.length > 0) {
this.formArray.removeAt(this.formArray.controls.indexOf(this.selectedFormControl));
Expand All @@ -79,14 +77,14 @@ export class MultiLanguageFormService {

if (this.inputValue === null) {
this.formArray.push(
this._fb.group({
this._fb.nonNullable.group({
language: this.availableLanguages[this.selectedLanguageIndex],
value: [newText, this.validators],
})
);
}

this.selectedFormControl.setValue(newText);
this.selectedLanguageControl.setValue(newText);
this.inputValue = newText;
}

Expand All @@ -97,22 +95,26 @@ export class MultiLanguageFormService {
changeLanguage(languageIndex: number) {
this.selectedLanguageIndex = languageIndex;

const newFormControl = this.formArray.controls.find(
const existingControl = this.formArray.controls.find(
control => control.value.language === this.availableLanguages[this.selectedLanguageIndex]
);

this.inputValue = newFormControl ? this.selectedFormControl.value : null;
this.inputValue = existingControl ? this.selectedLanguageControl.value : null;
}

private _setupLanguageIndex(): number {
const responseLanguages = (this.formArray.value as { language: string }[])
const responseLanguages = this.formArray
.getRawValue()
.map(v => v.language)
.filter(language => this.availableLanguages.includes(language));

const userFavoriteLanguage =
(this._store.selectSnapshot(UserSelectors.language) as string) || navigator.language.substring(0, 2);

if (responseLanguages.length === 0) {
if (!isDaschLanguage(userFavoriteLanguage)) {
return 0;
}
// form is empty, push a new value
const indexFavoriteLanguage = this.availableLanguages.indexOf(userFavoriteLanguage);

Expand All @@ -125,7 +127,11 @@ export class MultiLanguageFormService {
}
}

if (responseLanguages.includes(userFavoriteLanguage) && this.availableLanguages.includes(userFavoriteLanguage)) {
if (
isDaschLanguage(userFavoriteLanguage) &&
responseLanguages.includes(userFavoriteLanguage) &&
this.availableLanguages.includes(userFavoriteLanguage)
) {
return this.availableLanguages.indexOf(userFavoriteLanguage);
}

Expand Down

0 comments on commit 42d5112

Please sign in to comment.