How do I config styles or classes I want to use for controls or forms?

I am new to the library and trying to figure out how to apply styles to individual controls or forms. I am using material renderer.

Is it configurable as part of uischeme? I couldn’t find any info on styling in the documentation or example sections

Thanks

[original thread by zwjohn]

Thanks for your interest in JSON Forms. You can style the Material UI controls via their theme support. Besides theming and CSS styling the only way to influence the result would be to implement custom renderers which do exactly what you want.

[zwjohn]

Thanks Stefan, we use material theming for global theme, I was talking about some control level styling, or group level styling. I guess I have to do some research on custom renderer for that. I was hoping that css styles and css classes can be configured in the control element and group of uischema.

[zwjohn]

@sdirix I was wondering if there is way to set html id for each control or form? currently the control id is using scope value from uischema. Thanks

At the moment we don’t support setting ids or classes via the ui schema. However if that’s all you want to do then implementing a custom renderer is straight forward as you can just set your class and delegate everything else to the existing renderers. You can take a look at this tutorial for custom layout renderer which also just wraps an existing renderer.

[zwjohn]

@sdirix Thanks for the advise. I’ve tried on custom layout renderer from the the example, however, it didn’t work for me. There were two issues I encountered. 1. visible prop is always undefined, causing the the group became hidden, I had to remove hidden tag to see the group. 2. ExpansionPanelSummary is rendered after removing Hidden tag due to visible issue, however,ExpansionPanelDetails is not rendered

[zwjohn]

[zwjohn]

[zwjohn]

BTW, I am not using Redux, so I registered the renderer this way
const myGroupTester = rankWith(1000, uiTypeIs(“Group”));
materialRenderers.push( { tester: myGroupTester, renderer: MyGroupRenderer })

[zwjohn]

When I hard coded visible to true, then everything renders. my question would be: where and how visible prop is set?

const layoutProps = {
    elements: uischema.elements,
    schema: schema,
    path: path,
    direction: "column",
    visible: true,
    uischema: uischema,
    renderers: renderers
};

We use higher order components in JSON Forms to bring in state and functionality (validation, visibility, enablement etc.). From the code snippet it looks like you registered your “vanilla” renderer directly which will then only receive local props.

Try wrapping your renderer in withJsonFormsLayoutProps which you can import from @jsonforms/react. For example export const ConnectedMyGroupRenderer = withJsonFormsLayoutProps(MyGroupRenderer) and register that one instead.

[zwjohn]

[zwjohn]

@sdirix Thanks very much for the hint, updated the code with your suggestion, and it worked.
Another issue I am encountering. There is leftover container div when using Rule to hide an element. For example I added following rule, and the Birth Date became hidden after name matched the condition. However it leave a blank space on the UI
{
“type”: “Control”,
“label”: “Birth Date”,
“scope”: “#/properties/birthDate”,
“rule”: {
“effect”: “HIDE”,
“condition”: {
“scope”: “#/properties/name”,
“schema”: { “const”:“test” }
}
}
}

Our layout renderers create a material ui grid independently from what is actually rendered within them. This is usually pretty useful as the form doesn’t jump around when controls are made visible/hidden. If you want collapsing behavior you’ll need to implement a custom layout renderer. Within it you can do the visible calculation for each child and don’t render the grid item if the child is invisible. You can find the visible calculation here.

[zwjohn]

@sdirix once again, thanks for the feedback and information provided, I will try to do that. Also, I am trying to implement custom error message, so I installed ajv-errors package hoping providing some custom error messages. However, the custom error message may not have a dataPath if it’s top level, and therefore the error messages don’t show anymore once custom message is implemented. According to the author of AJV, this is expected as some required field can be on top level, and no dataPath needed. Please refer to this link about the discussion Missing dataPath attribute for required field · Issue #1133 · ajv-validator/ajv · GitHub . It seems that JsonForms wouldn’t render error message without dataPath, that’s why I am not getting error messages after implemented custom AJV with ajv-errors. Can you please take a look?
export const createAjv = (options) => {
const ajv = new AJV({
schemaId: ‘auto’,
allErrors: true,
jsonPointers: true,
messages:true,
…options
});
return ajv;
};
export const myAjv = new AJError(createAjv());
This is my testing schema
const schema = {
“type”: “object”,
“properties”: {
“name”: {
“type”: “string”,
“minLength”: 3,
“maxLength”:4,
“description”: “Please enter your name”,
},
“vegetarian”: {
“type”: “boolean”
},
“birthDate”: {
“type”: “string”,
“format”: “date”

        },
        "nationality": {
            "type": "string",
            "enum": [
                "DE",
                "IT",
                "JP",
                "US",
                "RU",
                "Other"
            ]
        },
        "personalData": {
            "type": "object",
            "properties": {
                "age": {
                    "type": "integer",
                    "description": "Please enter your age."
                },
                "height": {
                    "type": "number"
                },
                "drivingSkill": {
                    "type": "number",
                    "maximum": 10,
                    "minimum": 1,
                }
            },
            "required": [
                "age",
                "height"
            ],
            "errorMessage": {
                "required": {
                    "age":  "Age Missing",
                    "height":  "Height Missing ",

                }
            }
        },
        "occupation": {
            "type": "string"
        },
        "postalCode": {
            "type": "string",
            "maxLength": 5
        }
    },
    "required": [
        "occupation",
        "nationality",
        "name"
    ],
    "additionalProperties ": true,
    "errorMessage": {
        "type": 'should be an object',
        "required": {
            "name":  "Name Missing",
            "nationality":  "Nationality Missing ",
            "occupation":  "Occupation Missing ",
        }
    }

};

[zwjohn]

The different dataPath behavior doesn’t come from ajv-errors but because your AJV instance is configured differently to the one of JSON Forms. Especially we’re setting errorDataPath: ‘property’ as this configures Ajv to report for example missing properties on the property level and not on the object level containing that property.

You can also translate the error messages manually if you’d like a more direct access to them, see #1512 for more information.

[zwjohn]

@sdirix Thanks! just want to mention that according to the AJV documentation, errorDataPath was deprecated sometime ago, the default would be object. Also, I was wondering how do I call updateErrors in onChange when I am not using redux? Thanks!

just want to mention that according to the AJV documentation, errorDataPath was deprecated sometime ago, the default would be object

Good point, we need to handle that at some point. For now we expect the errors to contain property-scoped dataPaths. I created an issue for that.

Also, I was wondering how do I call updateErrors in onChange when I am not using redux?

In the standalone variant you’re only able to access the dispatch from within JSON Forms, for example via a custom renderer. We could think about exposing the dispatch method in the future also via the onChange mechanism. For now you could implement it yourself by using JsonFormsStateProvider + JsonFormsDispatch instead of the JsonForms component. You can check the implementation of the JsonForms component on how to do it as it’s simply a wrapper for this combination. There you can access the dispatch method in the context via the useJsonForms() hook, e.g. const { dispatch } = useJsonForms() and implement the error update with it.