gcanti / tcomb-form

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

Run this.refs.form.validate() without triggering error messages #306

Closed superplussed closed 8 years ago

superplussed commented 8 years ago

Hi @gcanti, thanks for your great work with this. I'm still learning about tcomb, tcomb-form, and tcomb-validations and how they all work together. But I haven't figured out if this is possible:

  1. Add an onChange to <t.form.Form />, and on each keystroke run this.refs.form.validate().
  2. Once the form has been confirmed as valid, enable the submit button.
  3. Only display error an message if a specific field has been onBlurred and is still invalid.

I've seen your code regarding onBlur given a specific field, but I haven't seen any way to check whether the entire form is valid without triggering all of the error messages. Is there a way to do this?

gcanti commented 8 years ago

Hi @superplussed,

Not sure if satisfies all your requirements but what about something like this?

const Type = t.struct({
  name: t.String,
  surname: t.String,
  age: t.maybe(t.Number)
})

const options = {
  fields: {
    name: { error: 'Invalid name' },
    surname: { error: 'Invalid surname' },
    age: { error: 'Invalid age' }
  }
}

function getRequiredFields(type) {
  return Object.keys(type.meta.props).filter(prop => type.meta.props[prop].meta.kind !== 'maybe')
}

const App = React.createClass({

  getInitialState() {
    const pendingValidations = getRequiredFields(Type)
    return {
      disabled: pendingValidations.length !== 0,
      pendingValidations
    }
  },

  onSubmit(evt) {
    evt.preventDefault()
    const v = this.refs.form.getValue()
    if (v) {
      console.log(v) // eslint-disable-line
    }
  },

  onChange(value, path) {
    if (this.state.pendingValidations.length === 0) {
      // validate the whole form
      const validation = this.refs.form.validate()
      // enable the button only if everything's ok
      this.setState({ value, disabled: !validation.isValid() })
    } else {
      // just validate tha changed field
      this.refs.form.getComponent(path).validate()
      // recalculate which fields are not yet validated
      const pendingValidations = this.state.pendingValidations.filter(f => f !== path[0])
      this.setState({ value, pendingValidations, disabled: pendingValidations.length !== 0 })
    }
  },

  render() {
    return (
      <form onSubmit={this.onSubmit}>
        <t.form.Form
          ref="form"
          type={Type}
          value={this.state.value}
          options={options}
          onChange={this.onChange}
        />
        <div className="form-group">
          <button disabled={this.state.disabled} type="submit" className="btn btn-primary">Save</button>
        </div>
      </form>
    )
  }

})