gcanti / tcomb-form

Forms library for react
https://gcanti.github.io/tcomb-form
MIT License
1.16k stars 136 forks source link

Creating List with dynamic field types #301

Closed amrut-bawane closed 8 years ago

amrut-bawane commented 8 years ago

I am using

I have two sets of field types which may be added to the list field. Upon invoking addFilterField should add FilterField to the list, similarly for addMetaField.

When I add a new field, the previously added fields (either Filter or Meta) all change to the same type that of the newly created one.

class MyForm extends Component {
  constructor() {
    super();
    this.state = {
      schema: this.generateSchema(),
      filterField: false,
      value: {
        formfields: []
      }
    };
    this.addFilter = this.addFilter.bind(this);
    this.generateSchema = this.generateSchema.bind(this);
    this.addMeta = this.addMeta.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  generateSchema() {
    const FilterField = t.enums.of([
        'Pattern',
        'Color',
        'Brand'
    ], 'FilterField');

    const MetaField = t.struct({
        title: t.String,
        identifier: t.String,
        canCreateVariant: t.Boolean,
        required: t.Boolean,
        isSearchFilter: t.Boolean,
        type: t.String,
        unit: t.String,
        minVal: t.String,
        maxVal: t.String,
        toolTip: t.String,
        placeHolder: t.String
      }, 'MetaField');

    const Field = t.union([FilterField, MetaField], 'Field');

    const that = this;
    Field.dispatch = value => {
      if(that.state.filterField) return FilterField;
      else return MetaField;
    }

    const Fields = t.list(Field);

    const Schema = t.struct({
      title: t.String,
      description: t.String,
      longName: t.String,
      canAddProducts: t.Boolean,
      formfields: Fields
    });
     return Schema;
  }

  onChange(value) {
    this.setState({value});
  }

  render() {
    return (
      <div>
        <t.form.Form ref="form" type={this.state.schema} value={this.state.value} onChange={this.onChange} />
        <div className="form-group">
          <button type="submit" className="btn btn-primary">Save</button>
          <button className="btn btn-primary" onClick={this.addFilter} >Add Filter Field</button>
          <button className="btn btn-primary" onClick={this.addMeta} >Add Metadata Field</button>
        </div>
      </div>
    );
  }

  addFilter() {
    this.setState({ filterField: true }, () => {
      this.setState({
        value: t.update(this.state.value, {
          formfields: {
            $push: [undefined]
          }
        })
      });
    });
  }

  addMeta() {
    this.setState({ filterField: false }, () => {
      this.setState({
        value: t.update(this.state.value, {
          formfields: {
            $push: [undefined]
          }
        })
      });
    });
  }

}
gcanti commented 8 years ago

It seems you forgot to port a couple of things from my example (https://github.com/gcanti/tcomb-form/issues/297#issuecomment-187637556):

This is an amended version of your code and works fine for me:

class MyForm extends React.Component {
  constructor() {
    super();
    this.state = {
      schema: this.generateSchema(),
      value: {
        formfields: []
      }
    };
    this.addFilter = this.addFilter.bind(this);
    this.generateSchema = this.generateSchema.bind(this);
    this.addMeta = this.addMeta.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  generateSchema() {
    const FilterField = t.enums.of([
        'Pattern',
        'Color',
        'Brand'
    ], 'FilterField');

    const MetaField = t.struct({
        title: t.String,
        identifier: t.String,
        canCreateVariant: t.Boolean,
        required: t.Boolean,
        isSearchFilter: t.Boolean,
        type: t.String,
        unit: t.String,
        minVal: t.String,
        maxVal: t.String,
        toolTip: t.String,
        placeHolder: t.String
      }, 'MetaField');

    const Field = t.union([FilterField, MetaField], 'Field');

    Field.dispatch = value => {
      if (t.Object.is(value)) {
        return MetaField;
      }
      return FilterField;
    };
    const Fields = t.list(Field);

    const Schema = t.struct({
      title: t.String,
      description: t.String,
      longName: t.String,
      canAddProducts: t.Boolean,
      formfields: Fields
    });
     return Schema;
  }

  onChange(value) {
    this.setState({value});
  }

  render() {
    return (
      <div>
        <t.form.Form ref="form" type={this.state.schema} value={this.state.value} onChange={this.onChange} />
        <div className="form-group">
          <button type="submit" className="btn btn-primary">Save</button>
          <button className="btn btn-primary" onClick={this.addFilter} >Add Filter Field</button>
          <button className="btn btn-primary" onClick={this.addMeta} >Add Metadata Field</button>
        </div>
      </div>
    );
  }

  addFilter() {
    this.setState({
      value: t.update(this.state.value, {
        formfields: {
          $push: [undefined]
        }
      })
    });
  }

  addMeta() {
    this.setState({
      value: t.update(this.state.value, {
        formfields: {
          $push: [{}]
        }
      })
    });
  }

}