prometheusresearch-archive / react-forms

Forms library for React.
1.16k stars 112 forks source link

Customize classNames using the new Mapping - Scalar API #74

Closed owais closed 9 years ago

owais commented 9 years ago

Judging from examples and test cases, I created something like this

    var schema = Mapping({
      title: Mapping({
        title: Scalar({label:'title'})
      }),

      description: Mapping({
        title: Scalar({label:'description'})
      }),

      dates: Mapping({className: 'myonclass'}, {
        start_date: Scalar({label:'start_date', className: 'okok'}),
        end_date: Scalar({label:'end_date'})
      }),

      hiring_contacts: Mapping({
        title: Scalar({label:'Hiring contacts'})
      })
    })

I want to add some special css classes to some fieldsets and fields. I guess this is out of the scope of Mappings. What is the recommended way to add extra classes fieldsets/input elements or customize the markup in general?

P.S. Great job on the APIs. I just looked at one example and was able to guess how the rest of the API would work. Very predictable design.

andreypopp commented 9 years ago

Hi,

the way you customize the markup of the form field is to assign a component prop to schema node:

Mapping({
  component: <ReactForms.Fieldset className="someCustomClassName" />
}, {
  ...
})
owais commented 9 years ago

@andreypopp Thanks a lot. I figured it out thanks to the great API design. I ran into another issue while trying to assign input a regular react component. The component did not receive all the props such as value. I guess it is expected and I've to subclass the field from react-form and implement my component as a derived class of that or am I missing something?

Once again, thanks a lot.

andreypopp commented 9 years ago

Can you provide an example of your issue with input? I\m sure it receives value and onChange props.

owais commented 9 years ago
    var user = <ReactSelectize
      onSelectizeChange={this.onChange}
      selectedValue={this.state.selected}
      items={this.state.items || []}
      valueField="id"
      labelField="name"
      searchField="name"
      preload={true}
      selectId="add-user-to-project"
      placeholder="Search for a user.."
      remoteUrl="/api/users/"
    />

    var schema = Mapping({
      user: Scalar({label:'Person', input: user})
    });

It did not receive value and onChange that were referred to during Selectize component initialization so I changed the names to onSelectizeChange and selectedValue. Kind of a workaround for now. Is value and onChange all that will not go through or is it supposed to happen to other attributes as well?

owais commented 9 years ago

ReactSelectize is my own component forked from https://github.com/ggarek/react-selectize

andreypopp commented 9 years ago

Hm... it should receive value/onChange. Could console.log(this.props) inside render() method of < ReactSelectize /> component?

owais commented 9 years ago

Printing value and onChange do show values but not the ones passed on by me. First time value is always undefined and after that on every change, value is always the current value of the field. onChange always receives a function callback but it is never the same callback as the one passed on from the parent component.

var Parent = react.createComponent({
   onChange: function(){
      console.log('onChange called');
   },
   render: function(){
      return <form><ReactSelectize onChange={this.onChange}/></form>
   }
});

>> This thing prints "onChange called"
var Parent = react.createComponent({
   onChange: function(){
      console.log('onChange called');
   },
   render: function(){
      var input = <ReactSelectize onChange={this.onChange}/>
      var schema = Mapping({label: "user", input: user});
      return <form><ReactSelectize onChange={this.onChange}/></form>
   }
});

>> This does not print anything

My guess is that Scalar overrides the onChange handler with it's own and that handler is used to validate the data.

andreypopp commented 9 years ago

Yes, that's right.

React Forms overrides value and onChange props of the input. If you want to perform your own action on onChange you must implement a custom version of input component:

var CustomSelectize = React.createClass({
  render() {
    return (
      <ReactSelectize {...this.props} onChange={this.onChange} />
    )
  },

  onChange(value) {
   console.log('New value', value)
    this.props.onChange(value);
  }
})

and then use it in your schema:

var schema = Mapping({label: "user"}, {
  user: Scalar({input: <CustomSelectize />})
});
owais commented 9 years ago

Thanks @andreypopp! Makes sense.

Wouldn't a better API be to call parent's onChange automatically if passed and may be value should be overwritten if explicitly supplied by the parent. It would eliminate a lot of possible confusion.

owais commented 9 years ago

Looks like specifying value to an input has a whole another meaning in react http://facebook.github.io/react/tips/controlled-input-null-value.html. It makes the field "readonly". react-forms should definitely support that if it doesn't already.

I still don't see any harm in auto calling parents onClick if present though.