rjsf-team / react-jsonschema-form

A React component for building Web forms from JSON Schema.
https://rjsf-team.github.io/react-jsonschema-form/
Apache License 2.0
14.13k stars 2.18k forks source link

Customizing ArrayFieldTemplate in order to use Material-ui Autocomplete to handle entire array with items #1960

Open OleksiL opened 4 years ago

OleksiL commented 4 years ago

Prerequisites

Description

Hello guys.

Seeking for advice - maybe it is simply unachievable and I should stick to the default solution.

By default rjsf renders 'array of strings' field as a set of separate widgets - each per item in the array. There are Add/Remove/Order buttons to manage items within array.

I would like to use Material-UI Autocomplete component (official doc) in order to handle the entire array. I want to make it look like this: array_with_items

I thought that ArrayFieldTemplate was the way to go, but I faced the following issues:

  1. I serialize schemes in a database as jsons. Setting ArrayFieldTemplate per field doesn't work for me - obviously that it can't serialize MyArrayFieldTemplate component and I don't see how to register it like a widget. "ui:ArrayFieldTemplate": MyArrayFieldTemplate

  2. After playing around with ArrayFieldTemplate it seems that it only allows to change how will be rendered already added items. It also allows to add a widget for a new item with onAddClick prop. But this method will not let me set the value - it only adds another item in items prop. Methods like onAddIndexClick seem to just add/remove items as widgets at certain indexes, but they don't allow to manipulate items values at these indexes.

Is it a dead idea to use Autocomplete in order to manage the entire array, and I should abandon it?

My ArrayFieldTemplate looks like this (I had to set it at form level to make it working):

import React from 'react';
import { Chip, TextField as MaterialTextField } from '@material-ui/core';
import { Autocomplete } from '@material-ui/lab';

export default function MyArrayTemplate(props) {

  function handleChange(event, value: string[]) {
    // how to pass value (strings array) from Autocomplete to formData as an array value?
    // props.formData = {...props.formData, [props.title]: value }; // formData is readOnly - doesn't work

    props.onAddClick(event); //adds new item to the array, but without value.
    // if I try to call props.items[someIndex].onAddIndexClick(value) it doesn't work either
  }

  function renderTags(value: string[], getTagProps) {
    return value.map((option: string, index: number) => (
      <Chip variant="outlined" label={option} {...getTagProps({ index })} />
    ))
  }

  function renderInput(params) {
    return (
      <MaterialTextField
        {...params}
        variant="outlined"
        error={false}
        required={false}
        label={'label'}
        disabled={false}/>)
  }

  return (
    <div>
      <Autocomplete
        fullWidth
        multiple
        options={[]}
        freeSolo
        onChange={handleChange}
        renderTags={renderTags}
        renderInput={renderInput}
      />
    </div>
  );
}

not working array template

stanlemon commented 4 years ago

Is it possible for you to throw together a working example somewhere? I'd be happy to dig in more.

OleksiL commented 4 years ago

Is it possible for you to throw together a working example somewhere? I'd be happy to dig in more.

thanks. I created a sandbox - https://codesandbox.io/s/autumn-cherry-qdtrz

stanlemon commented 4 years ago

This was very helpful, thank you!

The function props.onAddClick which you are calling is here: https://github.com/rjsf-team/react-jsonschema-form/blob/master/packages/core/src/components/fields/ArrayField.js#L283-L324

If you look closely there I think you'll see that it doesn't quite do what you want. It really doesn't use anything from the event, so you're not passing data when the new items are added.

You need a way to not just add an array item but also add data when doing so.

There's nothing in the API today to support this, but you might be able to set the value through some hackery like:

props.items[0].children.props.onChange("foo");

This is fiddling with internals, so proceed with caution, but I think you can accomplish what you're after this way.

OleksiL commented 4 years ago

Thanks a lot! I will try it on Monday.

skd09021991 commented 3 years ago

HI

I am also using Autocomplete of material-ui with react json schema-form , could not find any hackery to set the array values to the formData . can anyone please help me in this to set the values in the formData ..Thanks in advance

jmguzmanc commented 3 years ago

Hi @OleksiL Did you succed at making this work? We are looking for the same Thanks a lot JM

kbh4r4th commented 3 years ago

@OleksiL

I was dealing with the same issue but I found a workaround to deal with it.

Workaround for Json Form Array with React Autocomplete

  1. Replace array with string field
  2. Define a widget to customize the string field with react autocomplete [multiple]
  3. Define an array [react state] to store the selected items [autocomplete]
  4. Set value [autocomplete] to the array with a condition if array.len > 0 or props.value
  5. onChange [autocomplete]: keep updating the array with selected items
  6. onSubmit [autocomplete]: add a field (name must be different from #1) to the formData with the array

Voila! And that should output the data as an array.

rajesh0519 commented 1 year ago

Can u plz help me with onDropIndexClick , i think it is used to remove an element from array , but it is not working for me..!

jamiedruce commented 1 year ago

Did anyone here arrive at a working solution for using MUI Autocomplete components for array fields? Seems like a far neater UI over the default one implemented by the MUI theme.