Skip to content

Instantly share code, notes, and snippets.

@mrbongiolo
Last active August 29, 2015 14:18
Show Gist options
  • Save mrbongiolo/9f0b34efd90eec02ed99 to your computer and use it in GitHub Desktop.
Save mrbongiolo/9f0b34efd90eec02ed99 to your computer and use it in GitHub Desktop.
React and Rails
<!-- 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 }) %>
// 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>
);
}
});
// 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>
);
}
});
// 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>
);
}
});
// 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