Search code examples
javascriptreactjsreact-jsonschema-forms

Using JsonSchemaForm on change to update field's content


I am trying to use JsonSchema-Form component but i ran into a problem while trying to create a form that, after choosing one of the options in the first dropdown a secondary dropdown should appear and give him the user a different set o options to choose depending on what he chose in the first dropdown trough an API call.

The thing is, after reading the documentation and some examples found here and here respectively i still don't know exactly how reference whatever i chose in the first option to affect the second dropdown. Here is an example of what i have right now:

Jsons information that are supposed to be shown in the first and second dropdowns trough api calls:

Groups: [
{id: 1,
name: Group1}
{id: 2,
name: Group2}
]
User: [User1.1,User1.2,User2.1,User2.2,User3.1,User3.2, ....]

If the user selects group one then i must use the following api call to get the user types, which gets me the the USER json.

Component That calls JSonChemaForm

render(){
 return(
          <JsonSchemaForm
            schema={someSchema(GroupOptions)}
            formData={this.state.formData}
            onChange={{}}
            uiSchema={someUiSchema()}
            onError={() => {}}
            showErrorList={false}
            noHtml5Validate
            liveValidate
          > 
 )
}

SchemaFile content:

export const someSchema = GroupOptions => ({
  type: 'object',
  required: [
    'groups', 'users',
  ],
  properties: {
    groups: {
      title: 'Group',
      enum: GroupOptions.map(i=> i.id),
      enumNames: GroupOptions.map(n => n.name),
    },
    users: {
      title: 'Type',
      enum: [],
      enumNames: [],
    },
  },
});

export const someUISchema = () => ({
  groups: {
    'ui:autofocus': true,
    'ui:options': {
      size: {
        lg: 15,
      },
    },
  },
  types: {
    'ui:options': {
      size: {
        lg: 15,
      },
    },
  },

});

I am not really sure how to proceed with this and hwo to use the Onchange method to do what i want.


Solution

  • I find a solution for your problem.There is a similar demo that can solve it in react-jsonschema-form-layout. 1. define the LayoutField,this is part of the demo in react-jsonschema-form-layout.To make it easier for you,I post the code here. Create the layoutField.js.:

    import React from 'react'
    import ObjectField from 'react-jsonschema-form/lib/components/fields/ObjectField'
    import { retrieveSchema } from 'react-jsonschema-form/lib/utils'
    import { Col } from 'react-bootstrap'
    
    export default class GridField extends ObjectField {
        state = { firstName: 'hasldf' }
        render() {
            const {
                uiSchema,
                errorSchema,
                idSchema,
                required,
                disabled,
                readonly,
                onBlur,
                formData
            } = this.props
            const { definitions, fields, formContext } = this.props.registry
            const { SchemaField, TitleField, DescriptionField } = fields
            const schema = retrieveSchema(this.props.schema, definitions)
            const title = (schema.title === undefined) ? '' : schema.title
    
            const layout = uiSchema['ui:layout']
    
            return (
                <fieldset>
                    {title ? <TitleField
                        id={`${idSchema.$id}__title`}
                        title={title}
                        required={required}
                        formContext={formContext}/> : null}
                    {schema.description ?
                        <DescriptionField
                            id={`${idSchema.$id}__description`}
                            description={schema.description}
                            formContext={formContext}/> : null}
                    {
                        layout.map((row, index) => {
                            return (
                                <div className="row" key={index}>
                                    {
                                        Object.keys(row).map((name, index) => {
                                            const { doShow, ...rowProps } = row[name]
                                            let style = {}
                                            if (doShow && !doShow({ formData })) {
                                                style = { display: 'none' }
                                            }
                                            if (schema.properties[name]) {
                                                return (
                                                    <Col {...rowProps} key={index} style={style}>
                                                        <SchemaField
                                                            name={name}
                                                            required={this.isRequired(name)}
                                                            schema={schema.properties[name]}
                                                            uiSchema={uiSchema[name]}
                                                            errorSchema={errorSchema[name]}
                                                            idSchema={idSchema[name]}
                                                            formData={formData[name]}
                                                            onChange={this.onPropertyChange(name)}
                                                            onBlur={onBlur}
                                                            registry={this.props.registry}
                                                            disabled={disabled}
                                                            readonly={readonly}/>
                                                    </Col>
                                                )
                                            } else {
                                                const { render, ...rowProps } = row[name]
                                                let UIComponent = () => null
    
                                                if (render) {
                                                    UIComponent = render
                                                }
    
                                                return (
                                                    <Col {...rowProps} key={index} style={style}>
                                                        <UIComponent
                                                            name={name}
                                                            formData={formData}
                                                            errorSchema={errorSchema}
                                                            uiSchema={uiSchema}
                                                            schema={schema}
                                                            registry={this.props.registry}
                                                        />
                                                    </Col>
                                                )
                                            }
                                        })
                                    }
                                </div>
                            )
                        })
                    }</fieldset>
            )
        }
    }
    

    in the file, you can define doShow property to define whether to show another component. Next.Define the isFilled function in JsonChemaForm

    const isFilled = (fieldName) => ({ formData }) => (formData[fieldName] && formData[fieldName].length) ? true : false
    

    Third,after you choose the first dropdown ,the second dropdown will show up

    import LayoutField from './layoutField.js'
    const  fields={
       layout: LayoutField
    }
    const uiSchema={
        "ui:field": 'layout',
        'ui:layout': [
            {
                groups: {
                    'ui:autofocus': true,
                    'ui:options': {
                       size: {
                          lg: 15,
                     },
                  },
                }
            },
            {
                users: {
                    'ui:options': {
                        size: {
                            lg: 15,
                        },
                    },
                    doShow: isFilled('groups')
                }
            }
        ]
    }
    ...
    render() {
        return (
            <div>
                <Form
                    schema={schema}
                    uiSchema={uiSchema}
                    fields={fields}
                />
            </div>
        )
    }