jsonforms
jsonforms copied to clipboard
Duplicate validation messages in primitive array renderer
Describe the bug
For primitive arrays, all validation messages are combined into 1, and the top level validation for the array (is required, min amount of items) seems to duplicate itself.
The error property seems to contain:
- Validation messages for all items in the array
- Validation messages for the top level array itself (min/max items validation, is required, etc.)
Screenshots
Validation messages for the individual items are also in the top level array renderer:

Top level validation messages are duplicated:

Expected behaviour
I would expect the error property of the Array control to only contain validation for the top level. The individual items in the array could render their own validation messages.
Additionally, I would expect the "is required" or "should NOT have fewer than X items" validation messages to not be duplicated like this.
To Reproduce
Array layout renderer:
export class ArrayLayoutRenderer extends JsonFormsArrayControl implements OnInit {
private addItem: (path: string, value: any) => () => void;
private removeItems: (path: string, toDelete: number[]) => () => void;
constructor(jsonFormsService: JsonFormsAngularService) {
super(jsonFormsService);
}
remove(index: number): void {
this.removeItems(this.propsPath, [index])();
}
add(): void {
this.addItem(this.propsPath, createDefaultValue(this.scopedSchema))();
}
mapToProps(state: JsonFormsState): StatePropsOfArrayLayout {
const props = mapStateToArrayLayoutProps(state, this.getOwnProps());
return { ...props };
}
ngOnInit(): void {
super.ngOnInit();
const { addItem, removeItems } = mapDispatchToArrayControlProps(
this.jsonFormsService.updateCore.bind(this.jsonFormsService)
);
this.addItem = addItem;
this.removeItems = removeItems;
}
getProps(index: number): OwnPropsOfRenderer {
return {
schema: this.scopedSchema,
path: Paths.compose(this.propsPath, `${index}`),
uischema: controlWithoutLabel(`#`)
};
}
trackByFn(index: number) {
return index;
}
}
Template file:
<label class="h6">{{ label }}</label>
<p class="text-hint mt-2 mb-1">{{ uischema['description'] }}</p>
<p class="caption-2 text-danger my-1">{{ error }}</p>
<button (click)="add()">Add</button>
<ng-container *ngFor="
let item of [].constructor(data);
let idx = index;
trackBy: trackByFn;
last as last
">
<jsonforms-outlet [renderProps]="getProps(idx)"></jsonforms-outlet>
<button (click)="remove(idx)">delete</button>
</ng-container>
Schema for this individual question:
{
"list-question": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
}
}
Used Setup (please complete the following information):
- Framework: angular
- RendererSet: custom, but based on Angular Material
I've hacked around this a little bit for now. Basically splitting the error string, filtering out duplicates, and whitelisting the error messages I expect / want.
But this feels really hacky 😅
get errors(): string[] {
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
return this.error.split('\n')
.filter(onlyUnique)
.filter(message => {
return message.match(/should NOT have fewer than \d+ items/) != null
|| message.match(/should NOT have more than \d+ items/) != null
|| message.match(/is a required property/) != null
})
}
Hi! Thanks for the extensive report! I saw similar errors in React Material, so I'm pretty sure that this is an issue in the @jsonforms/core library.
If you'd like to you can check mapStateToArrayLayoutProps. Probably there something is going wrong with the error calculation. By fixing it in the core you don't need the hacky workaround ;). If you (or someone else) provides a good contribution (including test cases) we can quickly publish a new alpha release of the next version so you could then already consume the fix while waiting for the stable release.
Any update on this? The issue is still present.
Still seeing this:

We are experiencing similar problems while using angular-material. I did some digging around and as far as I can tell the problem is caused by the way 'child error messages' (error messages on sub elements of the array) are determined.
If you have a schema similar to this
{
"title": "My schema",
"type": "object",
"properties": {
"myArray": {
"type": "array",
"items": {
"type": "string"
}
},
"myArrayTwo": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
then any error on myArrayTwo will also be reported on myArray because myArrayTwo starts with the string myArray. This is as far as I can tell a bug in reducers/core.ts/subErrorsAt. I think if you change the matchPath callback in that method to include a . at the end (because you are interested in errors on sub properties), the problem should be fixed. The implementation of subErrorsAt should then look something like this:
export const subErrorsAt = (instancePath: string, schema: JsonSchema) =>
getErrorsAt(instancePath, schema, path => path.startsWith(instancePath + '.'));
Hi @FooBar08, Thanks for the analysis! Would you be willing to create a contribution?