20

https://marmelab.com/react-admin/Inputs.html#arrayinput Examples cover cases where you have an array of objects:

  backlinks: [
        {
            date: '2012-08-10T00:00:00.000Z',
            url: 'http://example.com/foo/bar.html',
        },
        {
            date: '2012-08-14T00:00:00.000Z',
            url: 'https://blog.johndoe.com/2012/08/12/foobar.html',
        }
   ]

is it possible to have it work with just an array of strings?

backlinks: ['a', 'b', 'c']
yBrodsky
  • 4,981
  • 3
  • 20
  • 31
  • Have you tried it and gotten an error? – Tholle Jul 22 '18 at 21:08
  • Yes. The thing is that I don't know what to put in the inner fields in the "source" attribute. With an object its `` – yBrodsky Jul 22 '18 at 22:42
  • 1
    You may need to create an alternative ArrayInput. Start with a copy and make modifications. Have a good look at the source. https://github.com/marmelab/react-admin/blob/master/packages/ra-ui-materialui/src/input/ArrayInput.js Also notice the fact that redux' FieldArray is used. – Christiaan Westerbeek Jul 23 '18 at 10:37
  • alright, will have a look that way – yBrodsky Jul 23 '18 at 12:51
  • 2
    @yBrodsky have you come up with something? Do you mind sharing? I am looking for the exact same thing, I have a simple flat array of emails (strings) that I want to be able to nicely edit, add or remove. Cheers! – Vojta Hejda Jul 25 '18 at 10:31
  • 2
    @VojtaHejda nah, I failed miserably. Tried to create my own component (with blackjack and hookers) using redux-form, but couldn't do it. Don't have much experience with the whole react thing. So I ended up using https://marmelab.com/react-admin/Inputs.html#referencearrayinput Works fine and does what I intended with the bonus that it shows you the related model data – yBrodsky Jul 25 '18 at 13:18

5 Answers5

12

I was able to execute the inputs variant, as opposed to the fields variant, by simply not providing a source attribute for the inner TextField, and sourcing the array in the actual ArrayField. Then of course just use a SimpleFormIterator. Clearly React favors the use of keys, treating array types like maps, for the most part.

<ArrayInput source="my-source">
  <SimpleFormIterator>
    <TextInput />
  </SimpleFormIterator>
</ArrayInput>
kcrawford
  • 121
  • 2
  • 6
  • 4
    I have tried to use this solution, but getting error like 'Failed prop type: Invalid prop `value` supplied to `TextField`.' – Aswathy Balan Feb 15 '19 at 07:05
  • @Aswathy - did you solve it? I get another error: `Error: Cannot set a numeric property on an object` though my array contains URLs such as "https://picsum.photos/800" – Hola Jun 06 '20 at 10:19
  • 3
    @AswathyBalan @Hola Use empy string for source `` – Penguin Jan 18 '21 at 03:16
  • Worth noting this fails for the ArrayField, but works for ArrayInput for some reason – polar May 30 '21 at 20:17
  • This solutiton worked for react-admin 3.17.2 - I just had to add a key to TextInput otherwise only the first element would render. A simple `key={Math.random()}` did the job. – dalmo Aug 17 '21 at 11:32
  • It's important to note that with empty source you will not be able to add a new element if element prior is falsey (example 0 for number input, or empty string for text input) – Krzysztof Krzeszewski Feb 01 '23 at 09:46
4

Here is my working code based on @fzaninotto's post in react-admin Issues:

import Chip from '@material-ui/core/Chip'

const TextArrayField = ({ record, source }) => {
  const array = record[source]
  if (typeof array === 'undefined' || array === null || array.length === 0) {
    return <div/>
  } else {
    return (
      <>
        {array.map(item => <Chip label={item} key={item}/>)}
      </>
    )    
  }
}
TextArrayField.defaultProps = { addLabel: true }

Usage:

  <TextArrayField source="tags">
    <SingleFieldList>
      <ChipField source="id" />
    </SingleFieldList>
  </TextArrayField>
Bagusflyer
  • 12,675
  • 21
  • 96
  • 179
  • Seems like this should also work as `` as the custom component isn't using the children passed to it. – mjomble Jul 04 '21 at 09:08
  • If you are struggling with nested values, replace the `record[source]` with this: ` import get from 'lodash/get'; ... const array = get(record, source); ... ` – mtyurt Aug 20 '21 at 11:58
1

Maybe you can create your own Field component which can able to take source and record as props.

 function populateList(numbers) {
        return numbers.map((number) =>
            <li key={number.toString()}>
                {number}
            </li>
        );
    }

    const SimpleArray = ({source, record = {}}) =>
        <ul>
            {
                populateList(record[source])
            }
        </ul>;


    SimpleArray.defaultProps = {
        addLabel: true,
        label: 'List'
     };


    SimpleArray.propTypes = {
        label: PropTypes.string,
        record: PropTypes.object,
        source: PropTypes.string
    };

    export default SimpleArray;

And easily use it inside any form element like :

  <SimpleShowLayout>
                        <TextField source="id"/>
                        <TextField label="Title" source="title" className={classes.name}/> 
                        <TextField source="title"/>
                        <NumberField source="defaultItemCount"/>
                        <RichTextField source="description"/>
                        <NumberField source="priceInNumber"/>
                        <SimpleArray source="attributeArray" label="Product Attributes" />




                    </SimpleShowLayout>
erhanasikoglu
  • 1,685
  • 1
  • 21
  • 33
0

My solution expands a bit on the answer from @kcrawford

In my case, I needed to output the array of URLs. Simplified version of the code

const MassMessageEdit: FC<any> = (props) => (
  <Edit {...props}>
    <SimpleForm {...props}>
      ...
      <ArrayField source="onesignalUrls">
        <URLs />
      </ArrayField>
    </CreateEditForm>
  </Edit>
)

const URLs: React.FC<{ ids?: string[] }> = (props) => {
  if (!props["ids"]) return null
  return (
    <ul>
      {props["ids"].map((link, key) => (
        <li key={key}>
          <a href={JSON.parse(link) as string} rel="noopener noreferrer" target="_blank">
            Notification {key + 1}
          </a>
        </li>
      ))}
    </ul>
  )
}

ArrayField passes the values array as ids, which can later be parsed and rendered

romants
  • 3,660
  • 1
  • 21
  • 33
0

This is how I did to get it work:

        <ArrayInput source="backlinks">
          <SimpleFormIterator inline>
            <TextInput source="." />
          </SimpleFormIterator>
        </ArrayInput>
Arsene
  • 1,037
  • 4
  • 20
  • 47