React-admin: [RFC] Embedded Arrays using FieldArray

Created on 23 May 2017  路  24Comments  路  Source: marmelab/react-admin

Regarding FieldArray Implementation (as per mentioned in #232 ) I have the following initial implementation:

               <EmbeddedManyInput source="links">
                    <TextInput source="url" />
                    <TextInput source="context"/>
                </EmbeddedManyInput>

That renders the following

screenshot-localhost-8080-2017-05-23-22-50-57

I didn't need to edit SimpleForm and TabbedForm. I've just added EmbeddedManyInput

import FlatButton from 'material-ui/FlatButton';
import React, { Component } from 'react';
import { FieldArray, Field } from 'redux-form';
import PropTypes from 'prop-types';

import ContentCreateIcon from 'material-ui/svg-icons/content/create';
import ActionDeleteIcon from 'material-ui/svg-icons/action/delete';

export class EmbeddedManyInput extends Component {
    constructor(props) {
        super(props);
        const { source } = props;
    }

    renderList = ({ fields }) => {
        const { children } = this.props;
        return (
            <div style={{ margin: '1em' }}>
                {fields.map((member, index) =>
                    <div key={index} style={{ display: 'inline-block', verticalAlign: 'bottom' }}>
                        <div style={{ padding: '0 1em 1em 1em' }}>
                            {React.Children.map(children, input => input && (
                                <div key={input.props.source} className={`aor-input-${input.props.source}`} style={input.props.style}>
                                    <Field {...input.props} name={`${member}.${input.props.source}`} component={input.type} />
                                </div>
                            ))}
                        </div>
                        <FlatButton
                            label="Remove"
                            icon={<ActionDeleteIcon />}
                            onClick={() => fields.remove(index)} />
                    </div>
                )}
                <FlatButton icon={<ContentCreateIcon />} label="Add" onClick={() => fields.push({})} />
            </div>
        )
    }


    render() {
        const { input, resource, label, source, allowEmpty, basePath, onChange, children, meta } = this.props;

        return (
            <FieldArray name={source} component={this.renderList} />
        )
    }
}

EmbeddedManyInput.propTypes = {
    addField: PropTypes.bool.isRequired,
    allowEmpty: PropTypes.bool.isRequired,
    basePath: PropTypes.string,
    children: PropTypes.node,
    label: PropTypes.string,
    meta: PropTypes.object,
    onChange: PropTypes.func,
    input: PropTypes.object,
    resource: PropTypes.string,
    source: PropTypes.string,
};

EmbeddedManyInput.defaultProps = {
    addField: false,
    allowEmpty: false,
};

export default EmbeddedManyInput;

What do you think?

enhancement

Most helpful comment

Nice! I think this could make it to the core. But I'd prefer that subrecords are displayed as lines rather than columns. Also, active buttons (like the Add and Remove buttons in your screenshot) should use the active color (blue in that theme). Lastly, instead of using <Field>, use admin-on-rest's FormField, which already does that.

If other contributors like the idea, go for it and open a PR!

All 24 comments

Nice! I think this could make it to the core. But I'd prefer that subrecords are displayed as lines rather than columns. Also, active buttons (like the Add and Remove buttons in your screenshot) should use the active color (blue in that theme). Lastly, instead of using <Field>, use admin-on-rest's FormField, which already does that.

If other contributors like the idea, go for it and open a PR!

It is exactly what I was looking for, I too prefer subrecords displayed as lines.

Just what I was looking for! I will try this out.

That's what I need. Thank you for your work

This is an exciting add-on to admin-on-rest. I think a prop in the EmbeddedInput controlling maximum number of fields to display would be nice too. What do you think !? @MhdSyrwan

I know this is committed against next but should it also work with master? The PR diff - https://patch-diff.githubusercontent.com/raw/marmelab/admin-on-rest/pull/697.diff - seems to apply properly to both master and next branches. But when I try to use the <EmbeddedArrayInput> component against a patched master branch, I get:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check your code at [line in custom JS where I use <EmbeddedArrayInput>].

Here is my implementation. I've tried different variations based on examples. What I've done here seems to be the most recent example:

import { List, Edit, Create, Datagrid, ReferenceField, TextField, EditButton, DisabledInput, LongTextInput, ReferenceInput, SelectInput, SimpleForm, TextInput, AutocompleteInput, SimpleList, EmbeddedArrayInput } from 'admin-on-rest';

(code removed)

export const AudioEdit = (props) => (
  <Edit title={<UpdateTitle />} {...props}>
    <SimpleForm>
      <DisabledInput source="id" />
      <ReferenceInput label="User" source="ownerId" reference="users">
        <SelectInput optionText="username" />
      </ReferenceInput>
      <TextInput source="title" />
      <LongTextInput source="description" />
      <EmbeddedArrayInput source="sources">
        <SimpleList>
          <TextInput source="type" />
          <TextInput source="uri" />
          <TextInput source="bitrate" />
        </SimpleList>
      </EmbeddedArrayInput>
      <TextInput source="pathAlias" />
      <DisabledInput source="updated" />
      <DisabledInput source="created" />
    </SimpleForm>
  </Edit>
);

I've tried using the AOR next branch to test this with my app that I've already done a lot of work on, but there seems to be some pretty major changes that really break things. (Maybe I'm doing something wrong but from what I've read here - https://github.com/marmelab/admin-on-rest/issues/639#issuecomment-300165386 - the next branch is not nearly ready for primetime so using it in my app doesn't make sense.) EDIT: Ignore this comment. #639 has nothing to do with this issue.

So what's the recommended way of using this and contributing fixes/improvements?

You're mixing up the mui next branch (not ready) and the air branch (ready).

@fzaninotto My apologies. You're right - issue #639 that I referenced has nothing to do with this. I was too hasty in referring to that. Please disregard that comment.

But my issue still remains. The moment I switch to AOR next branch, clear my node_modules dir, npm install and then npm start, I get these errors (this was error free when using master branch):

Failed to compile.

Error in ./src/rest/dubplate-rest.js
Module not found: 'admin-on-rest/lib/util/fetch' in /[project path]/src/rest

@ ./src/rest/dubplate-rest.js 7:13-52

Error in ./src/rest/dubplate-rest.js
Module not found: 'admin-on-rest/lib/rest/types' in /[project path]/src/rest

@ ./src/rest/dubplate-rest.js 9:13-52

Error in ./src/App.js
Module not found: 'admin-on-rest' in /[project path]/src

@ ./src/App.js 16:19-43

Error in ./src/audio.js
Module not found: 'admin-on-rest' in /[project path]/src

@ ./src/audio.js 13:19-43

Error in ./src/users.js
Module not found: 'admin-on-rest' in /[project path]/src

@ ./src/users.js 13:19-43

Error in ./src/authClient.js
Module not found: 'admin-on-rest' in /[project path]/src

@ ./src/authClient.js 7:19-43

Here is my package.json. Do any other versions need to change?

{
  "name": "admin",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^15.5.4",
    "react-dom": "^15.5.4"
  },
  "devDependencies": {
    "admin-on-rest": "github:marmelab/admin-on-rest#next",
    "react-scripts": "^0.9.5"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

It's currently difficult to reference aor with its github address. It will not have the dist folder nor the makefile which is required to build it.

See https://github.com/marmelab/admin-on-rest#contributing

Thank you @djhi. I followed https://github.com/marmelab/admin-on-rest#contributing and I now have the <EmbeddedArrayInput> element working in my project.

After playing around with it for a while, something that seems like an obvious addition is the ability to re-order. @MhdSyrwan do you have any thoughts on re-ordering? Would https://github.com/react-dnd/react-dnd be a good candidate for this?

awesome feature. Easy to plug in and works as expected. I would go for horizontal layout, but thats easily changeable.

@nickrobillard It looks like an interesting feature to be added, I will take a look at the react-dnd to see how it could be integrated.

@astriskit Why would we need to limit the number of fields, can you give an example use case ?

+1

@MhdSyrwan
I was working on a project, where I had to create field to record the key and value for the webhook feature. I found the EmbeddedInput quite helpful, but clientele asked to limit the key-value pairs to just some predefined number. Anyway, it was the motivation behind the suggestion. :)
I hope it serves as a use-case.

@astriskit So, you want to limit key-value pairs not the fields. yes it's a nice feature to be added.

@MhdSyrwan Oh yeah. :) Say, I want to just record (or get input from user) for a maximum of two key-value pair and minimum of one( so minimum also can be another prop :) ). Then one key and value fields will always be visible and not more than two(pairs) can be added.

@MhdSyrwan I am pretty eager to use this, once the reordering functionality is there. And I'd love to contribute in order to make it happen! Have you begun work on it or should I take a look?

@christianpbrink I'm writing tests for it now, but regarding the re-ordering i don't know if i could have the time to do it
check https://github.com/marmelab/admin-on-rest/pull/697

Ok nice. Thank you. I haven't played around with it myself yet -- does it support any kind of reordering functionality, or inserting a new item at a specific index?

Unfortunately not yet

what do you think about support in this component attached structures such as:

        <EmbeddedManyInput source="questions">
          <TextInput source="text"/>
          <EmbeddedManyInput source="answers">
            <TextInput source="text" />
          </EmbeddedManyInput>
        </EmbeddedManyInput>

?

@iamsimakov
Now it does support nested structure. check #697

As explained in #697, this is not something we'll have in core.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marknelissen picture marknelissen  路  3Comments

pixelscripter picture pixelscripter  路  3Comments

yangjiamu picture yangjiamu  路  3Comments

ericwb picture ericwb  路  3Comments

rkyrychuk picture rkyrychuk  路  3Comments