Last active
August 29, 2015 14:18
-
-
Save mrbongiolo/9f0b34efd90eec02ed99 to your computer and use it in GitHub Desktop.
React and Rails
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- app/views/associates/edit/_tab_relatives.html.erb --> | |
<!-- The render method is used to get the same data for the resourcesCollection | |
as I would get if I did a GET request later on to associates/relatives path | |
TODO: use a Decorator or something like it to fetch this data without the need | |
to render a view. --> | |
<%= react_component('RelativesPage', { | |
resourcesCollectionJSONInitialData: render( | |
template: 'relatives/index', | |
formats: :json, | |
locals: { :@relatives => @associate_relatives }), | |
filiationsCollection: Relative::Form.filiations_collection, | |
url: associate_relatives_path(@associate) }, | |
{ prerender: true }) %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// app/assets/javascripts/components/relatives/form.js.jsx | |
var RelativeForm = React.createClass({ | |
componentDidMount: function() { | |
$( this.refs.date_of_birth.getDOMNode() ).inputmask('d/m/y', { | |
clearIncomplete: true, | |
oncomplete: function(event) { | |
this.handleChange('date_of_birth', event); | |
}.bind(this), | |
onincomplete: function(event) { | |
event.target.value = ''; | |
this.handleChange('date_of_birth', event); | |
}.bind(this), | |
oncleared: function(event) { | |
event.target.value = ''; | |
this.handleChange('date_of_birth', event); | |
}.bind(this) | |
}); | |
$( this.getDOMNode() ).find('[data-toggle=tooltip]').tooltip(); | |
}, | |
componentWillUnmount: function() { | |
$( this.getDOMNode() ).find('[data-toggle=tooltip]').tooltip('destroy'); | |
}, | |
handleChange: function (fieldName, event) { | |
event.preventDefault(); | |
this.props.onFormInputChange(fieldName, event.target.value); | |
}, | |
handleSubmit: function(e) { | |
e.preventDefault(); | |
this.props.onFormSubmit(); | |
}, | |
render: function() { | |
var groupClassesFor = function(key, otherClasses) { | |
var defaults = { | |
'form-group': true, | |
'has-error': this.props.formErrors[key] !== undefined | |
}; | |
return classNames( _.extend({}, defaults, otherClasses) ); | |
}.bind(this); | |
var hasErrorMessageFor = function(key) { | |
var error = this.props.formErrors[key]; | |
if (error) { | |
return error.join(' '); | |
} else { | |
return ''; | |
} | |
}.bind(this); | |
var formTitle = (function() { | |
if (this.props.formIsEditingIndex !== null) { | |
return <h2 className="title-separator m-t-none"><i className="fa fa-edit"></i> Editando Dependente</h2>; | |
} else { | |
return <h2 className="title-separator m-t-none"><i className="fa fa-plus"></i> Novo Dependente</h2>; | |
}; | |
}.bind(this))(); | |
var rowName = (function() { | |
return( | |
<div className={groupClassesFor('name', {'col-md-5': true})}> | |
<label className="control-label" htmlFor="relative_name">Nome</label> | |
<input className="form-control input-lg" type="text" ref="name" id="relative_name" | |
value={this.props.formData.name} onChange={this.handleChange.bind(this, 'name')} | |
data-toggle="tooltip" data-original-title={hasErrorMessageFor('name')} /> | |
</div> | |
); | |
}.bind(this))(); | |
var filiationsCollectionOptionsForSelect = this.props.filiationsCollection.map(function(key, index) { | |
return <option value={ key[1] } key={ key } >{ key[0] }</option> | |
}.bind(this)); | |
var rowFiliation = (function() { | |
return ( | |
<div className={groupClassesFor('filiation', {'col-md-3': true})}> | |
<label className="control-label" htmlFor="relative_filiation">Filiação</label> | |
<select className="form-control input-lg" ref="filiation" id="relative_filiation" | |
value={this.props.formData.filiation} onChange={this.handleChange.bind(this, 'filiation')} | |
data-toggle="tooltip" data-original-title={hasErrorMessageFor('filiation')}> | |
<option value="">Selecione</option> | |
{ filiationsCollectionOptionsForSelect } | |
</select> | |
</div> | |
); | |
}.bind(this))(); | |
var rowDateOfBirth = (function() { | |
return ( | |
<div className={groupClassesFor('date_of_birth', {'col-md-2': true})}> | |
<label className="control-label" htmlFor="relative_date_of_birth">Data de Nascimento</label> | |
<input className="form-control input-lg" type="text" ref="date_of_birth" id="relative_date_of_birth" | |
value={this.props.formData.date_of_birth} onChange={this.handleChange.bind(this, 'date_of_birth')} | |
data-toggle="tooltip" data-original-title={hasErrorMessageFor('date_of_birth')} /> | |
</div> | |
); | |
}.bind(this))(); | |
var rowActions = (function() { | |
return ( | |
<div className="form-group col-md-2"> | |
<button name="button" type="submit" className="btn btn-primary btn-sm m-t-lg m-r-xs"> | |
{ RelativesPage.isSubmitingOrText(this, <span><i className="fa fa-check"></i> Salvar</span>) } | |
</button> | |
<a onClick={this.props.onFormReset} className="btn btn-white btn-sm m-t-lg">Cancelar</a> | |
</div> | |
); | |
}.bind(this))(); | |
return ( | |
<div className="relativeForm"> | |
{ formTitle } | |
<form role="form" onSubmit={this.handleSubmit} id="relativeForm"> | |
<input type="hidden" ref="id" value={this.props.formData.id} /> | |
<div className="row"> | |
{ rowName } | |
{ rowFiliation } | |
{ rowDateOfBirth } | |
{ rowActions } | |
</div> | |
</form> | |
</div> | |
); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// app/assets/javascripts/components/relatives/line.js.jsx | |
var RelativesLine = React.createClass({ | |
componentDidMount: function() { | |
$( this.getDOMNode() ).find('[data-toggle=tooltip]').tooltip(); | |
}, | |
componentWillUnmount: function() { | |
$( this.getDOMNode() ).find('[data-toggle=tooltip]').tooltip('destroy'); | |
}, | |
render: function() { | |
var trClasses = classNames({ | |
'is-editing': this.props.relativeIsEditing | |
}); | |
return ( | |
<tr onDoubleClick={this.props.onRelativeEdit} className={trClasses}> | |
<td>{ this.props.relative.id }</td> | |
<td>{ this.props.relative.name }</td> | |
<td>{ this.props.relative.filiation }</td> | |
<td>{ this.props.relative.date_of_birth }</td> | |
<td className="tcol-actions"> | |
<a onClick={this.props.onRelativeEdit} className="btn btn-white btn-xs m-r-xs" data-toggle="tooltip" title="Editar"><i className="fa fa-edit"></i></a> | |
<a onClick={this.props.onRelativeDestroy} data-confirm="Você tem certeza?" className="btn btn-white btn-xs" data-toggle="tooltip" title="Excluir"><i className="fa fa-trash-o"></i></a> | |
</td> | |
</tr> | |
); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// app/assets/javascripts/components/relatives/page.js.jsx | |
var RelativesPage = React.createClass({ | |
getInitialState: function() { | |
return { | |
relatives: [], | |
relativeFormIsEditingIndex: null, | |
relativeFormData: RelativesPage.BLANK_RELATIVE_FORM_DATA, | |
relativeFormErrors: {}, | |
relativeFormKey: 0, | |
isSubmiting: false | |
}; | |
}, | |
statics: { | |
BLANK_RELATIVE_FORM_DATA: (function() { | |
return({ | |
id: '', | |
name: '', | |
filiation: '', | |
date_of_birth: '' | |
}); | |
})(), | |
isSubmitingOrText: function(component, text) { | |
if( component.props.isSubmiting ) { | |
return(<i className="fa fa-spinner fa-spin"></i>); | |
} else { | |
return(text); | |
} | |
} | |
}, | |
componentDidMount: function() { | |
this.setState({ | |
relatives: JSON.parse( this.props.resourcesCollectionJSONInitialData ) | |
}) | |
}, | |
makeAjaxRequest: function(opts) { | |
if( this.state.isSubmiting ) { | |
return false; | |
} | |
this.setState({ isSubmiting: true }); | |
var defaultOpts = { | |
relative_id: '', | |
type: 'GET', | |
data: {}, | |
showMessages: false, | |
successMessage: 'Dependente salvo com sucesso!', | |
errorMessage: 'Ops! Não foi possível salvar este dependente, verifique os erros e tente novamente.' | |
}, | |
options = _.extend({}, defaultOpts, opts), | |
url = this.props.url + '/' + options['relative_id']; | |
$.ajax({ | |
url: url, | |
dataType: 'json', | |
type: options['type'], | |
data: options['data'], | |
success: function(data) { | |
if (options['showMessages']) { | |
showSuccessMessage(options['successMessage']); | |
} | |
this.setDefaultRelativePageStates({ relatives: data }); | |
}.bind(this), | |
error: function(xhr, status, err) { | |
if (options['showMessages']) { | |
showErrorMessage(options['errorMessage']); | |
} | |
this.setState({ | |
relativeFormErrors: xhr.responseJSON.errors, | |
isSubmiting: false | |
}); | |
}.bind(this) | |
}); | |
}, | |
handleRelativeEdit: function(index) { | |
this.state.relativeFormKey++; | |
this.setState({ | |
relativeFormIsEditingIndex: index, | |
relativeFormData: this.state.relatives[index], | |
relativeFormErrors: {} | |
}); | |
$.scrollTo( 'max', 300, { | |
onAfter: function() { $('#relative_name').focus(); } | |
}); | |
}, | |
handleRelativeDestroy: function(index) { | |
this.makeAjaxRequest({ | |
relative_id: this.state.relatives[index].id, | |
type: 'DELETE', | |
showMessages: true, | |
successMessage: 'Dependente excluído com sucesso!', | |
errorMessage: 'Ops! Não foi possível excluir este dependente.' | |
}); | |
}, | |
handleRelativeFormSubmit: function() { | |
var isEditing = this.state.relativeFormIsEditingIndex !== null, | |
relativeData = this.state.relativeFormData; | |
this.makeAjaxRequest({ | |
relative_id: isEditing ? relativeData.id : '', | |
type: isEditing ? 'PATCH': 'POST', | |
data: { relative: relativeData }, | |
showMessages: true | |
}); | |
}, | |
handleRelativeFormInputChange: function(fieldName, fieldValue) { | |
var stateToChange = { relativeFormData: {} } | |
stateToChange['relativeFormData'][fieldName] = {$set: fieldValue} | |
var nextState = React.addons.update(this.state, stateToChange); | |
this.setState(nextState); | |
}, | |
handleRelativeFormReset: function() { | |
this.setDefaultRelativePageStates(); | |
}, | |
setDefaultRelativePageStates: function(otherStates) { | |
var defaultStates = { | |
relativeFormIsEditingIndex: null, | |
relativeFormData: RelativesPage.BLANK_RELATIVE_FORM_DATA, | |
relativeFormErrors: {}, | |
isSubmiting: false | |
}, | |
nextStates = _.extend({}, defaultStates, otherStates); | |
this.state.relativeFormKey++; | |
this.setState(nextStates); | |
}, | |
render: function() { | |
return ( | |
<div className="relativesPage"> | |
<RelativesTable | |
isSubmiting={this.state.isSubmiting} | |
onRelativeEdit={this.handleRelativeEdit} | |
onRelativeDestroy={this.handleRelativeDestroy} | |
relatives={this.state.relatives} | |
relativeIsEditingIndex={this.state.relativeFormIsEditingIndex} /> | |
<RelativeForm | |
isSubmiting={this.state.isSubmiting} | |
onFormSubmit={this.handleRelativeFormSubmit} | |
onFormInputChange={this.handleRelativeFormInputChange} | |
onFormReset={this.handleRelativeFormReset} | |
formData={this.state.relativeFormData} | |
formErrors={this.state.relativeFormErrors} | |
formIsEditingIndex={this.state.relativeFormIsEditingIndex} | |
filiationsCollection={this.props.filiationsCollection} | |
key={'ID-' + this.state.relativeFormKey} /> | |
</div> | |
); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// app/assets/javascripts/components/relatives/table.js.jsx | |
var RelativesTable = React.createClass({ | |
render: function() { | |
var relativeLines = _.map(this.props.relatives, function (relative, index) { | |
var isEditing = (index === this.props.relativeIsEditingIndex); | |
return ( | |
<RelativesLine | |
isSubmiting={this.props.isSubmiting} | |
relative={relative} | |
relativeIsEditing={isEditing} | |
onRelativeEdit={this.props.onRelativeEdit.bind(null, index)} | |
onRelativeDestroy={this.props.onRelativeDestroy.bind(null, index)} | |
key={'ID-' + relative.id} /> | |
); | |
}.bind(this)); | |
return ( | |
<div className="relativesTable"> | |
<h2 className="title-separator m-t-none"><i className="fa fa-male"></i> Dependentes</h2> | |
<table className="table table-hover pax-table"> | |
<thead> | |
<tr> | |
<th title="ID">#</th> | |
<th>Nome</th> | |
<th>Filiação</th> | |
<th>Data de Nascimento</th> | |
<th className="tcol-actions">Ações</th> | |
</tr> | |
</thead> | |
<tbody> | |
{ relativeLines } | |
</tbody> | |
</table> | |
</div> | |
); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment