Using ajv-errors to override messages for required fields

I’m having some difficulty using ajv-errors to override the messages displayed for required fields. I have ajv-errors working properly for a field with a RegEx pattern (for example, Social Security Number); it displays my custom message as the user types each character, until it is complete. But the messages for required fields (by default, “is a required property”) are not displaying at all; I have not been able to override the messages. Meanwhile, the default info icon with a badge number next to it is still appearing above the table for array fields, and the tooltip shows the messages “should have required property ‘prop1’ should have required property ‘prop2’ . . .” as it always did.

Is there a trick to overriding the message display for required fields? I’d expect it to work like it’s working for my social security number field.

Also, is there an option to turn off the info icon / badge that is rendered at the top of a table?

[original thread by Kevin Ilsen]

[Kevin Ilsen]

Follow up: the problem seems to be related to the latest versions of ajv (v7)and ajv-errors (v2). When I downgraded to v1 of ajv-errors and used createAjv() in JsonForms (rather than directly using ajv), the basic error message overrides started working. However, I’m still unclear on how to eliminate the icon / badge and tooltips that are rendered with the table.

At the moment JSON Forms expects at least errorDataPath: ‘property’ to bet set. You can see our current configuration here. Does it work with ajv v7 and ajv-errors 2 when you set these options?

Also, is there an option to turn off the info icon / badge that is rendered at the top of a table?

This is hard coded at the moment. To remove it you’ll need to register a custom renderer. If you’d like you could also contribute a ui schema option to turn the summary off.

The custom renderer is straightforward to implement as you can just reuse the existing one and just remove the errors before handing over, e.g. (untested)

import { MaterialArrayControlRenderer } from '@jsonforms/material-renderers/lib/complex/MaterialArrayControlRenderer';
const MyMaterialArrayControlRenderer = (props: ArrayLayoutProps) => {
  const {errors, ...rem} = props;
  return <MaterialArrayControlRenderer {...rem}/>
}
export default withJsonFormsArrayLayoutProps(MyMaterialArrayControlRenderer);

[Kevin Ilsen]

It seems that v7 doesn’t support the errorDataPath setting. That could be the root of the problem.

[Kevin Ilsen]

I tried overriding the MaterialArrayControlRenderer but it didn’t help. In fact, that component does not appear to be the one that is showing the ValidationIcon – it’s not anywhere in the runtime component tree. Rather, it appears that MaterialArrayLayout is the class that contains the icon. Can I override the MaterialArrayLayout renderer in a similar fashion? Can you suggest how to define the tester?

Ah I see. Yes we have two different array renderers, the MaterialArrayControlRenderer (rendering the array as a table when applicable) and the MaterialArrayLayout which renders it as collapsible elements.

However the code should look exactly the same as in the example I posted above, just instead of MaterialArrayControlRenderer you can use MaterialArrayLayoutRenderer.

The tester could look like this:

export const myMaterialArrayLayoutTester: RankedTester = rankWith(
  5, // the original array layout is registered with 4
  isObjectArrayWithNesting
)

Note that isObjectArrayWithNesting is just a convenience function we provide from the core library. You can also hand over any custom self-written function in case you would like to be called in more (or less) cases than the original MaterialArrayLayoutRenderer.

[Kevin Ilsen]

Thanks again. Yes, I was able to make it work with that tester, as well as one like this, to basically override the rank of t he original renderer:

export const errorFreeArrayLayoutTester: RankedTester = withIncreasedRank(
    1,
    materialArrayLayoutTester
);

I can confirm that my renderer does indeed get used. However, I’m getting an error when I try to use the original renderer within my own, and the error seems to imply that the original renderer is not exported properly (which I find hard to believe). Here is the full implementation of my renderer:

import React from 'react';
import {RankedTester, withIncreasedRank} from '@jsonforms/core';
import {withJsonFormsArrayLayoutProps} from '@jsonforms/react';
import {MaterialArrayLayoutRenderer, materialArrayLayoutTester} from '@jsonforms/material-renderers';

/**
 * ErrorFreeArrayLayoutRenderer - hide the errors by not passing them to the standard renderer.
 * (The standard renderer always shows them, it is hard-coded with no option to hide it.)
 * @param props
 * @returns {JSX.Element}
 */
const errorFreeArrayLayoutRenderer = props => {
  console.log(props);
  const {errors, ...rest} = props;
  return (
      <MaterialArrayLayoutRenderer {...rest} />
  );
};

export default withJsonFormsArrayLayoutProps(errorFreeArrayLayoutRenderer);

export const errorFreeArrayLayoutTester: RankedTester = withIncreasedRank(
    1,
    materialArrayLayoutTester
);

The error that I get at display time is: “Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it’s defined in, or you might have mixed up default and named imports.”

If I change my component and simply return

hello
instead of the MaterialArrayLayoutRenderer, I get no error. So there’s something wonky about the way I’m importing and/or using that component, I just can’t seem to figure it out.

The problem is that the MaterialArrayLayoutRenderer which is default exported from the material-renderers is the one which is already connected to the JSON Forms context (this is what withJsonFormsXXX does) and therefore has a different argument set than the “normal” MaterialArrayLayoutRenderer. You want the unwrapped version of the MaterialArrayLayoutRenderer, however at the moment we only export the unwrapped controls and none of the complex or layout renderers. This is why in the example above I imported the renderer from the raw lib path.

I created the issue #1687 to fix this at some point. For now you can just import it from the @jsonforms/material-renderers/lib/xyz path.

[Kevin Ilsen]

Unfortunately, I’m unable to import the renderer directly from the lib path. So I ended up taking an altogether different approach to hiding the display of the error badge, via CSS:

.MuiBadge-root {
  display: none !important;
}

Seems to be the easiest way :wink: If you want to do it programmatically you could probably also do it via Material Ui’s theming customization, i.e.

const theme = createMuiTheme({
  overrides: {
    MuiBadge: {
      root: {
        display: 'none',
      },
    },
  },
});

and add it to your ThemeProvider. However I did not try it in practice.