Ability to add/remove optional properties

Hello,
I’m using JsonForms in my project and it’s working very well !
But today I need to integrate a new json schema with a lot of optional properties within an object (>30).
I’m using MaterialUI renderers and an uischema of type “Control” to generate the form for this object.
What I get is a form with an input for each property (more than 30 input).
I would like to be able to display only required or non-empty fields of my object, and to add/remove optional fields when needed (with a property selection dropdown, for instance).
I haven’t be able to achieve this with a custom renderer or custom control. Is there any way to achieve this ?

[original thread by DuncanTls]

Hi @duncantls. Thanks for your positive feedback :wink:

The object control is basically nothing more than a shortcut. It just takes all its properties and adds them to a layout. This is useful for prototyping and small objects but is just not flexible enough for larger objects or more complex use cases, as you noticed :wink:

I would like to suggest to create your ui schema manually. For example instead of

type: 'Control',
scope: '#/properties/myobject'

you could use

type: 'VerticalLayout',
elements: [
  {
    type: 'Control',
    scope: '#/properties/myobject/properties/name'
  },
  {
    type: 'Control',
    scope: '#/properties/myobject/properties/age'
  }
...

This way you will have more control about the layout of your form.

Depending on your use case you could hide/show or enable/disable each control with our built-in rule support. If these are not expressive enough you will need to implement your own ui schema generation. This ui schema can then be used for example by dispatching an action, e.g. Actions.setUischema(uischema) or actually implementing an own custom control for object and using it there instead of the default generated one.

[DuncanTls]

Thanks for your answer. Starting from your example, I implemented a custom layout renderer (following the tutorial from the official website).
From there, I can easily control what uischema.element to display or hide, according to selected properties in a dropdown.I can also automatically display non-empty fields by resolving my data against each ui element scope. My class looks like the following :

class OptionalLayoutRenderer extends React.Component  {

    constructor(props) {
        super(props);
        this.state = {
            selectedProperties: [] // Control what properties "Controls" are visible
        }
    }

    componentDidMount() {
    for(element of this.props.uischema.elements) //Initally, display automatically non-empty properties
    {
        Resolve.data(this.props.data, toPath(element.scope)) 
        //+ if non-empty add element to selectedProperties and update state
        }
    }

    handleSelectProperty = (property) => {
       //Add or remove property from selectedProperties and update state
    }

    render() {
        return (
            <div>
        <MyDropDown onClick={this.handleSelectProperty} ... />
        this.state.selectedProperties.map(element => <MaterialLayoutRenderer elements={[element]} .../>); 
            </div>
        );
    }
}
export default withJsonFormsLayoutProps(OptionalLayoutRenderer);
export const OptionalLayoutTester= rankWith(1000, uiTypeIs("OptionaLayout"));

The only thing I haven’t been able to achieve with this approach is to modify the data : my purpose would be to remove a property from my data when a property is unselected from the dropdown (in handleSelectProperty). The props handleChange from Control Renderers does not seems to be available in my Layout Renderer. Is there a way to improve this class to reach my goal ?

The data lives in the context / redux state. We have an update action with which you can modify the data. In controls you get a dispatch-update injected (handleChange) but for layouts you’ll need to do it yourself. You can access dispatch (and build your own handleChange) by building your own version of withJsonFormsLayoutProps.

[DuncanTls]

As you suggested, I implemented my own withJsonFormsLayoutProps to inject the handleChange prop to my renderer. Now I can modify my data from my class. Works like a charm ! Thanks for your support !