javascript - How do I get my components to return a serialization of themselves? -
i still learning react , javascript thank patience.
i trying serialize form data can send ruby on rails end processing. using vanilla react no additional depedencies flux, redux, etc.
it seems child components not returning , not quite sure why.
i have tried:
- exposing values through use of refs (but failed , read isn't idea anyway)
- exposing parent method within child components gather information each individual component (what see in jsfiddle).
- updating component states through onchange methods , trying access states of each child component
my jsfiddle: https://jsfiddle.net/morahood/0ptdpfu7/91/
i missing key element of react design patterns here. way off track? how can on track? able serialize form data in following format
{ "service_request" : { "services" : [ { "service_item" : ["oil change", "new windshield wipers"], "customer" : "troy", "manufacturer" : "ford", "model" : "f150" }, { "service_item" : ["tire rotation"], "customer" : "dave", "manufacturer" : "hyundai", "model" : "genesis" } ] } }
components
var serviceform = react.createclass({ render: function() { return ( <form onsubmit={ this.handleformsubmission }> { this.state.serviceitems.map(function(item) { return (item); })} <div classname="btn btn-default btn-sm" onclick={ this.addserviceitem }>+ append new service item</div> <button type="submit" classname="btn btn-success btn-lg pull-right">submit</button> </form> ); }, getinitialstate: function() { return ({ serviceitems: [<serviceitem serializeserviceitem={ this.serializeserviceitem } />] }); }, handleformsubmission: function() { console.log("form submitted!"); alert("serialized form data: " + this.serializeformdata()); }, addserviceitem: function(event) { var serviceitems = this.state.serviceitems; serviceitems.push(<serviceitem serializeserviceitem={ this.serializeserviceitem } />); this.setstate({ serviceitems: serviceitems }); }, serializeserviceitem: function() { var jsondata = { "service_item" : this.state.service_items, "customer" : this.state.customer, "manufacturer" : this.state.manufacturer, "model" : this.state.model } return (jsondata); }, serializeformdata: function() { return( this.state.serviceitems.map(function(item) { return (item.serializeserviceitem); })); } }); var serviceitem = react.createclass({ render: function() { return ( <div classname="row"> <div classname="col-sm-3"> <div classname="form-group"> <label>service item </label> <select multiple name="service_item" selected={ this.state.service_items } classname="form-control"> <option>oil change</option> <option>tire rotation</option> <option>new wiper blades</option> </select> </div> </div> <div classname="col-sm-3"> <div classname="form-group"> <label>customer </label> <select name="customer" selected={ this.state.customer } classname="form-control"> <option>troy</option> <option>dave</option> <option>brandon</option> </select> </div> </div> <div classname="col-sm-3"> <div classname="form-group"> <label>manufacturer </label> <div classname="input-group"> <input name="manufacturer" value={ this.state.manufacturer } onchange={ this.setmanufacturer } type="text" classname="form-control" /> </div> </div> </div> <div classname="col-sm-3"> <div classname="form-group"> <label>model </label> <div classname="input-group"> <input name="model" value={ this.state.model } onchange={ this.setmodel } type="text" classname="form-control" /> </div> </div> </div> </div> ); }, getinitialstate: function() { return({ service_items: [], customer: "", manufacturer: "", model: "" }); }, setmodel: function(event) { this.setstate({ model: event.target.value }); }, setmanufacturer: function(event) { this.setstate({ manufacturer: event.target.value }); }, setcustomer: function(event) { this.setstate({ customer: event.target.selected }); }, setserviceitems: function(event) { this.setstate({ service_items: event.target.selected }); } }); reactdom.render( <serviceform />, document.getelementbyid('container') );
solution
you "might" way overcomplicating things here. dom element <form>
can treated array of inner <input>
elements. in other words, if have:
render: function() { return ( <form ref="form"> ... </form> ); }
all input elements can accessed by:
serialized = {} (var in this.refs.form) { var input = this.refs.form[i]; serialized[input.name] = input.value; }
this might not provide enough flexibility. better solution might define methods in component instances return input values:
var serviceform = react.createclass({ serializeformdata: function() { return { foo: this.refs.foo.serialize() }; }, render: function() { var foo = this.state.foo; return ( <serviceitem data={foo} ref="foo" /> ); } }); var serviceitem = react.createclass({ serialize: function() { return { model: this.refs.model.value, ... } }, render: function() { var model = this.props.data.model; return ( <input ref="model" value={model} ... /> ); } });
if need multiple service items, you'll need rely on this.props.children
access each component instance rather on this.refs
:
var servicecontainer = react.createclass({ collectformdata: function() { return this.refs.form.serialize(); }, renderserviceitem: function(item, i) { return ( <serviceitem data={item} key={i} /> ); }, render: function() { // assuming you've moved state logic servicecontainer var serviceitems = this.state.serviceitems; return ( <serviceform ref="form"> {serviceitems.map(this.renderserviceitem)} </serviceform> ); } }); var serviceform = react.createclass({ serialize: function() { return react.children.map(this.props.children, function(item) { return item.serialize(); }); }, render: function() { return ( <div>{this.props.children}</div> ); } }); var serviceitem = react.createclass({ serialize: function() { // can still access input elements through refs in here ... }, render: function() { ... } });
note i'm using react.children
here rather using this.props.children
because when there's single child, children
not array (see: https://facebook.github.io/react/tips/children-props-type.html).
Comments
Post a Comment