foxhound87 / mobx-react-form

Reactive MobX Form State Management
https://foxhound87.github.io/mobx-react-form
MIT License
1.09k stars 129 forks source link

Dynamic Fields: Add new input fields on button click #315

Closed soodvaibhav closed 7 years ago

soodvaibhav commented 7 years ago

Hi, I am working on a form where the user inputs some set of data and if requires then they can click on add button to create more input fields. Is it possible with mobx-react-form to dynamically add more input fields on click?

foxhound87 commented 7 years ago

Yes, you can add fields dynamically.

On the demo repository, you can find an example, it uses a select input to create new fields, but you can start from that code and change it for your needs:

https://github.com/foxhound87/mobx-react-form-demo/blob/master/src/components/forms/FormDynamicFieldsSelect.jsx

soodvaibhav commented 7 years ago

Thanks for your quick response. :+1:

foxhound87 commented 7 years ago

You are welcome!

fsolar commented 6 years ago

Hi, I have a similar question, I need to insert a group of inputs (7 in total) with every onClick event, is this possible with mobx-react-form? and there will be any problem with the values of the repeated inputs?

MRMTC commented 6 years ago

Hi Guys :)

I'm trying to get the demo working on my end, and I'm having difficulty getting the "dynamic field" populated correctly. I have looked at the documentation and the issues that have been previously closed, but I just can't get why I'm not getting it to populate. From what I can tell the code should be working.

Below is a sample of my code. I have confirmed that the "fieldFactory" values are being populated correctly based on what I type/select on the dropdown; but the "dynamicFields" 'form' is just not being populated. I've also tried adding in fields to the "dynamicFields" but that didn't result in any success. Apologies for having all my code in one place - I find this helps me to understand how the libraries are working, I'll then refactor it on my end :)

Any guidance which can be provided would be appreciated. Please let me know if you need any furter information in order to assist.

Thanks.

`import React, { Component } from 'react';

import MobxReactForm from 'mobx-react-form'; import validatorjs from 'validatorjs'; import { observer } from 'mobx-react';

import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import getMuiTheme from 'material-ui/styles/getMuiTheme' import TextField from 'material-ui/TextField/TextField';

import { DropdownList } from 'react-widgets'; import 'react-widgets/dist/css/react-widgets.css';

import { Creatable } from 'react-select'; import 'react-select/dist/react-select.css'; import _ from 'lodash';

import { Button } from 'react-bootstrap';

import './Register.css';

//Define a plugins object and pass the installed package to the DVR property const plugins = { dvr: { package: validatorjs, extend: ($validator) => { //Here we can access the validatorjs instance to create custom error messages var messages = $validator.getMessages('en'); messages.required = 'Please complete this field in order to continue.'; messages.between = ':attribute must be between :min and :max characters long.'; messages.email = 'Please ensure you enter a valid :attribute to continue.' messages.digits = 'Please ensure you enter a valid number to continue.' $validator.setMessages('en', messages); }, }, };//end plugins

//Define dropdown select items const mealOptions = [ 'None', 'Vegan', 'Vegetarian', 'Halal', 'Kosher', 'Gluten-Free', ];//end mealOptions

//Define options for drop down (dynamic) const extra = [ { value: 'foo', label: 'foo' }, { value: 'bar', label: 'bar' }, { value: 'baz', label: 'baz' }, ];

//Define a fields object to describe the form fields which will contain the Inputs Labels and the DVR Rules. const fields = { Name: { rules: 'required|string|between:2,25', },

    Surname: {
        rules: 'required|string|between:2,25',
    },    

    email: {
        type: 'text',
        rules: 'required|email|string|between:5,100',
    },

    phoneNumber: {
        rules: 'required|digits:10',
    },  

    numerOfAdults: {
        rules: 'required|digits:1',
    }, 

    numberOfKids: {
        rules: 'required|digits:1',
    },   

    mealOptions: {
        label: 'Products - Dropdown (react-widgets)',
        value: 'None',
        extra: mealOptions, //https://github.com/foxhound87/mobx-react-form-demo/blob/master/src/components/inputs/WidgetDropdownList.jsx
    },

    fieldFactory: {
        onChange: fieldFactory => (values) => {
            const dynamicFields = fieldFactory.state.form.$('dynamicFields');

            const $values = _.chain(values)
              .mapValues('value')
              .values()
              .value();

            // use "extra" field prop to maintain current values
            const current = dynamicFields.get('extra') || [];
            const diff = _.difference($values, current);
            dynamicFields.set('extra', $values);

            // add dynamic fields
            diff.map((item) => {
              dynamicFields.add({ key: item });
              dynamicFields.$(item).set('placeholder', item);
              return null;
            });

            // remove unwanted items
            const allDynamicFields = dynamicFields.map(field => field.name);
            const fieldsToDelete = _.remove(allDynamicFields, item => !_.includes($values, item));
            fieldsToDelete.map(field => dynamicFields.del(field));
            fieldFactory.set('value', values);
          },
        },

    dynamicFields: [
      ],
};//end fields

//Validation Hooks which will be executed on submit after the validation is done. This is what the "hooks" object houses. const hooks = {
onSuccess(form) { // get field values console.log('Form Values!', form.values()); },

    onError(form) {
    // get all form errors
        console.log('All form errors', form.errors());
        // invalidate the form with a custom error message
        form.invalidate('This is a generic error message!');
    },

};//end hoooks

//Create a new LoginForm object passing all the previously created objects to it const form = new MobxReactForm({ fields }, { plugins, hooks });

//Define input box theme const muiTheme = getMuiTheme({ // fontFamily: 'CustomCraft-Regular', fontSize: 40, palette: { textColor: '#000000', //text color of input box primary1Color: '#7DA1BF', //floating label and underline color primary2Color: '#7DA1BF', primary3Color: '#FFCA28', // accent1Color: pinkA200, // accent2Color: grey100, // accent3Color: grey500, // alternateTextColor: white, // canvasColor: white, // borderColor: grey300, disabledColor: '#9E9E9E', //Text color when not clicked on pickerHeaderColor: '#7DA1BF', //clockCircleColor: fade(darkBlack, 0.07), //shadowColor: fullBlack, //errorColor: red500, }, });//end muiTheme

//@observer //Added at the bottom with export! class Register extends Component{

constructor (props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
}//end constructor

handleChange(event_value) {
    //console.log(event_value);
    form.$('mealOptions').value = event_value;
}//end handleChange

render () {

    return (
        <div>
            <MuiThemeProvider muiTheme={muiTheme} >
                <form className="formClass" noValidate autoComplete="on">       

                    <div className="ListItems">
                        <Creatable
                            multi={true}
                            allowCreate={true}
                            resetValue={[]}
                            options={extra}
                            openOnFocus={false}
                            placeholder="Type to add fields"
                            noResultsText="Type to add fields"
                            onChange={form.$('fieldFactory').onChange}
                            {...form.$('fieldFactory').bind()}
                        />
                    </div>

                    <br/><br/><h4>{form.$('dynamicFields').label}</h4>
                    {form.$('dynamicFields').map(field =>
                        <TextField
                            {...field.bind()} />,
                    )}

                    <TextField
                        hintText="Enter your name here..."
                        floatingLabelText="Name"
                        type="Name"
                        autoComplete="Name" 
                        errorText={form.$('Name').error}
                        {...form.$('Name').bind()} /><br></br>

                    <TextField
                        hintText="Enter your surname here..."
                        floatingLabelText="Last Name"
                        type="Surname" 
                        autoComplete="Surname"
                        errorText={form.$('Surname').error}
                        {...form.$('Surname').bind()} /><br></br>

                    <TextField              
                        hintText="Enter your email address here..."
                        floatingLabelText="Email"
                        type="email" 
                        autoComplete="Email"
                        errorText={form.$('email').error}
                        {...form.$('email').bind()} 
                        /><br></br>

                    <TextField
                        hintText="Enter your phone number here..."
                        floatingLabelText="Phone Number"
                        type="Phone number" 
                        autoComplete="Tel"
                        errorText={form.$('phoneNumber').error}
                        {...form.$('phoneNumber').bind()} /><br></br>

                    <TextField
                        hintText="Number of adults attending..."
                        floatingLabelText="Number of Adults"
                        type="Adult number" 
                        autoComplete="AdultNum"
                        errorText={form.$('numerOfAdults').error}
                        {...form.$('numerOfAdults').bind()} /><br></br>                             
                    <TextField
                        hintText="Number of kids attending..."
                        floatingLabelText="Number of Kids"
                        type="Kid number" 
                        autoComplete="KidNum"
                        errorText={form.$('numberOfKids').error}
                        {...form.$('numberOfKids').bind()} /><br></br>  

                    <div className="ListItems">
                        <label
                            className="ListItemsLabel"
                            >Meal Preference
                        </label>

                        <DropdownList
                            id="meal_list"
                            defaultValue="None"
                            value={form.$('mealOptions').value}
                            onChange={ this.handleChange }
                            data={mealOptions}
                        /><br></br> 
                    </div>

                    <Button 
                        bsStyle="info"
                        type="submit" 
                        onClick={form.onSubmit}>Submit</Button>
                    <Button 
                        bsStyle="info"
                        onClick={form.onClear}>Clear</Button><br></br>              

                    <div className="f6 db red">
                        {form.error}
                    </div>
                </form>      

            </MuiThemeProvider>

        </div>

    );//end return

}//end render

}//end Register Class

export default observer(Register);`