gcanti / tcomb-form

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

Custom List Component #270

Closed sockhead closed 8 years ago

sockhead commented 8 years ago

I love the work you've done on creating this library and really like how easy it is to build React forms using it. However, I'm running into a bit of hard time trying to create a component for my form.

I'm using the react-select-box as my means of retrieving input similar to a multiple select component. https://github.com/instructure-react/react-select-box

The issue I'm having is that react-select-box returns back an array of strings as its value and I can't seem to figure out how to set that as the value of a list. I"ve tried overwriting the template for the list, but that just results in a rendered react-select-box that does not make calls to onChange when it should. If I set the type as t.Str and overwrite the template with the same function then I get a working component aside from the fact that it does not pass validation and my form returns null when I try to submit.

The code below is for an unresponsive component. The component does not change state when values are selected.

const Options = {
  fields: {
    servicesUsed: {
      template: function(locals) {
        return (
          <CheckboxSelect multiple label="Account Types" value={locals.value} onChange={locals.onChange}>
             { stringToOptions(accountTypes) }
          </CheckboxSelect>
        ); 
      }
    } 
  } 
};

const Types = {
  servicesUsed: t.list(t.Str)
}; 

The code below is for a responsive component that does not pass validation due to the value being returned being the wrong type (this works as expected).

const Options = {
  fields: {
    servicesUsed: {
      template: function(locals) {
        return (
          <CheckboxSelect multiple label="Account Types" value={locals.value} onChange={locals.onChange}>
             { stringToOptions(accountTypes) }
          </CheckboxSelect>
        ); 
      }
    } 
  } 
};

const Types = {
  servicesUsed: t.Str
}; 

What I'm looking for is the functionality of the 2nd code block that also passes validation.

I'm not entirely sure what the best course of action would be to get what I'm looking for. I've looked into custom components, factories, and templates and still don't have a full grasp on which one would be the best way to go.

sockhead commented 8 years ago

I figured it out.

The version of tcomb-form that I'm using is 0.5.6 (so quite a few versions back) and the structure to get to the correct factories that I was attempting to use varied from the documentation I could find.

I ended up doing the following:

const Options = {
  fields: {
    servicesUsed: {
      label: 'Services Used',
      factory: t.form.Textbox,
      template: function(locals) {
        var classNames = 'form-group has-feedback' + (locals.hasError ? ' has-error' : null);
        return (
          <div className={classNames}>
            { locals.label ? <label className="control-label">{locals.label}</label> : null }
            <CheckboxSelect className="nopadding form-control" multiple label="Account Types" value={locals.value} onChange={locals.onChange}>
             { stringToOptions(accountTypes) }
          </CheckboxSelect>
        ); 
      }
    } 
  } 
};

const Types = {
  servicesUsed: t.list(t.Str)
}; 
gcanti commented 8 years ago

Hi,

There's a branch for each version, just in case you'd need some old documentation (file GUIDE.md):

https://github.com/gcanti/tcomb-form/branches

sockhead commented 8 years ago

I was looking at the guide for version 0.5, but there was no mention of factories like there was in the version 0.4 guide.

gcanti commented 8 years ago

Ah you are right! No mention of factories in v0.5's docs, I didn't notice. Sorry for the inconvenience