Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/app/init.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
TransferState,
Type,
} from '@angular/core';
import { DYNAMIC_FORM_CONTROL_MAP_FN } from '@ng-dynamic-forms/core';
import {
DYNAMIC_FORM_CONTROL_MAP_FN,
DYNAMIC_VALIDATORS,
} from '@ng-dynamic-forms/core';
import {
select,
Store,
Expand Down Expand Up @@ -41,6 +44,7 @@ import { LocaleService } from './core/locale/locale.service';
import { HeadTagService } from './core/metadata/head-tag.service';
import { CorrelationIdService } from './correlation-id/correlation-id.service';
import { dsDynamicFormControlMapFn } from './shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-map-fn';
import { CUSTOM_VALIDATORS } from './shared/form/builder/parsers/field-parser';
import { MenuService } from './shared/menu/menu.service';
import { MenuProviderService } from './shared/menu/menu-provider.service';
import { ThemeService } from './shared/theme-support/theme.service';
Expand Down Expand Up @@ -120,6 +124,10 @@ export abstract class InitService {
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
useValue: dsDynamicFormControlMapFn,
},
{
provide: DYNAMIC_VALIDATORS,
useValue: CUSTOM_VALIDATORS,
},
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
*/
openDropdown(sdRef: NgbDropdown) {
if (!this.model.readOnly) {
this.group.markAsUntouched();
this.inputText = null;
this.updatePageInfo(this.model.maxOptions, 1);
this.loadOptions(false);
Expand Down Expand Up @@ -339,4 +338,9 @@ export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyCom
this.currentValue = result;
}

onBlur(event: Event) {
super.onBlur(event);
this.group.markAsTouched();
}

}
43 changes: 42 additions & 1 deletion src/app/shared/form/builder/parsers/field-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
Inject,
InjectionToken,
} from '@angular/core';
import {
AbstractControl,
ValidatorFn,
} from '@angular/forms';
import {
DynamicFormControlLayout,
DynamicFormControlRelation,
Expand Down Expand Up @@ -48,6 +52,28 @@
*/
export const REGEX_FIELD_VALIDATOR = new RegExp('(\\/?)(.+)\\1([gimsuy]*)', 'i');

/**
* Define custom form validators here
*
* Register them by adding their key to a model's validator property, e.g:
* ```ts
* model.validators = Object.assign({}, model.validators, { notRepeatable: null });
* ```
*/
export const CUSTOM_VALIDATORS = new Map<string, ValidatorFn>([
['notRepeatable', notRepeatableValidator],
]);

export function notRepeatableValidator(control: AbstractControl) {
const value = control.value;

Check warning on line 68 in src/app/shared/form/builder/parsers/field-parser.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/form/builder/parsers/field-parser.ts#L68

Added line #L68 was not covered by tests
if (!Array.isArray(value) || value.length < 2) {
return null;

Check warning on line 70 in src/app/shared/form/builder/parsers/field-parser.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/form/builder/parsers/field-parser.ts#L70

Added line #L70 was not covered by tests
}
return {

Check warning on line 72 in src/app/shared/form/builder/parsers/field-parser.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/form/builder/parsers/field-parser.ts#L72

Added line #L72 was not covered by tests
notRepeatable: true,
};
}

export abstract class FieldParser {

protected fieldId: string;
Expand Down Expand Up @@ -131,7 +157,11 @@
},
};

return new DynamicRowArrayModel(config, layout);
const model = new DynamicRowArrayModel(config, layout);
if (config.notRepeatable) {
this.addNotRepeatableValidator(model);

Check warning on line 162 in src/app/shared/form/builder/parsers/field-parser.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/form/builder/parsers/field-parser.ts#L162

Added line #L162 was not covered by tests
}
return model;

} else {
const model = this.modelFactory(this.getInitFieldValue());
Expand Down Expand Up @@ -426,6 +456,17 @@
{ required: this.configData.mandatoryMessage });
}

protected addNotRepeatableValidator(controlModel) {
controlModel.validators = Object.assign({}, controlModel.validators, { notRepeatable: null });
controlModel.errorMessages = Object.assign(

Check warning on line 461 in src/app/shared/form/builder/parsers/field-parser.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/form/builder/parsers/field-parser.ts#L460-L461

Added lines #L460 - L461 were not covered by tests
{},
controlModel.errorMessages,
{
notRepeatable: 'error.validation.not.repeatable',
},
);
}

protected setLabel(controlModel, label = true, labelEmpty = false) {
if (label) {
controlModel.label = (labelEmpty) ? '&nbsp;' : this.configData.label;
Expand Down
2 changes: 1 addition & 1 deletion src/app/shared/form/form.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export class FormService {
});
}

field.markAsUntouched();
field.markAsUntouched({ onlySelf: true });
}

public resetForm(formGroup: UntypedFormGroup, groupModel: DynamicFormControlModel[], formId: string) {
Expand Down
3 changes: 3 additions & 0 deletions src/app/submission/sections/form/section-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@
* @private
*/
private inCurrentSubmissionScope(field: string): boolean {
if (isNotEmpty(this.sectionMetadata) && !this.sectionMetadata.includes(field)) {
return false;

Check warning on line 302 in src/app/submission/sections/form/section-form.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/submission/sections/form/section-form.component.ts#L302

Added line #L302 was not covered by tests
Copy link
Copy Markdown
Member

@ybnd ybnd Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Atmire-Kristof @alexandrevryghem apparently this change causes a regression for #1671 / #2252

For submission fields without a <linked-metadata-field> the Relationship list under the field is no longer updated on select/delete. You can see this in the Person form Publication field or by unlinking the Publication form author field from dc.contributor.author.

Can you find a way to reconcile both cases?

}
const scope = this.formConfig?.rows.find((row: FormRowModel) => {
if (row.fields?.[0]?.selectableMetadata) {
return row.fields?.[0]?.selectableMetadata?.[0]?.metadata === field;
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/en.json5
Original file line number Diff line number Diff line change
Expand Up @@ -1910,6 +1910,8 @@

"error.validation.required": "This field is required",

"error.validation.not.repeatable": "This field only accepts one value. Please discard the additional ones.",

"error.validation.NotValidEmail": "This is not a valid email",

"error.validation.emailTaken": "This email is already taken",
Expand Down
Loading