gcanti / tcomb-form

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

Default one list item open when list is empty (also validation of required fields within list) #326

Closed sagannotcarl closed 8 years ago

sagannotcarl commented 8 years ago

Version

Tell us which versions you are using:

I have a list that is made up of of an item that has two fields. The list itself and the fields within the list are all required. When the component loads I would like the initial state to display the two empty fields.

Also, If I first click the "add" button and validate the form then it correctly does not validate but if I don't click "add" the form incorrectly validates.

I could see other valid use case of wanting to validate only after the "add" button is clicked, but I think that functionality would map well to whether or not the initial t.list() was required or not.

Maybe this could be solved with overriding the template for the list but I couldn't figure out what the default template looks like to override.

Actual behaviour

For a required t.list(), a user must click on the "add" button first before seeing fields.

Even for required fields nested within a list the user can submit the form without entering anything in the fields. After clicking the "add" button validation correctly prevents submitting the form.

Steps to reproduce

const playAuthorSchema = t.struct({
  name: t.String,
  id: t.String,
});

const playSchema = t.struct({
  name: t.String,
  author: t.list(playAuthorSchema),
  about: t.maybe(t.String),
});

return (
  <form className="play-edit-form" onSubmit={this.handleSubmit.bind(this)} autocomplete="off" >
    <Form
      ref="form"
      type={playSchema}
      options={formOptions}
      value={this.state.play}
      onChange={this.onChange}
    />

    <button
      type="submit"
      className="edit-play-save"
    >Save</button>
  </form>
);

Stack trace and console log

Hint: it would help a lot if you enable the debugger ("Pause on exceptions" in the "Source" panel of Chrome dev tools) and spot the place where the error is thrown

gcanti commented 8 years ago

if I don't click "add" the form incorrectly validates

In general an empty list is acceptable, does validate by design. You can specify how many elements are required using a refinement:

const playAuthorSchema = t.struct({
  name: t.String,
  id: t.String,
})

const atLeastOne = arr => arr.length > 0

const playSchema = t.struct({
  name: t.String,
  author: t.refinement(t.list(playAuthorSchema), atLeastOne),
  about: t.maybe(t.String),
})

const Type = playSchema

const options = {
  fields: {
    author: {
      error: 'at least one'
    }
  }
}

I would like the initial state to display the two empty fields

You can provide an initial value in this.state.play:

getInitialState() {
  return {
    play: {
      author: [
        {}
      ]
    }
  }
}
sagannotcarl commented 8 years ago

Perfect @gcanti! And for me the second change (initializing an empty author in state) solved both of my problems because then the required field validation kicked in. I thought I tried that but I think I used curly brackets instead.

Thanks!

gcanti commented 8 years ago

Ok, though the user might click the "remove" button and you end up with no items again. I think that the refinement is mandatory if you want at least one item

sagannotcarl commented 8 years ago

Ah yes, good point. Thanks again for your help!