First, let’s create a form for adding new records to our grid.
CreateFormComponent.js
:
class CreateForm extends React.Component {
constructor(props) {
super(props);
this.form = new UIKernel.Form();
this.state = this.form.getAll(); //get all data from the Form Service
this.onFormChange = this.onFormChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// ..
}
Inside the constructor we’ve defined the form
prop and bound the onFormChange
and handleSubmit
methods.
The value of form
is an instance of UIKernel.Form
.
// ...
componentDidMount() {
this.form.init({
fields: ['name', 'surname', 'phone', 'age', 'gender'],
model: new UIKernel.Adapters.Grid.ToFormCreate(model, { //second parameter is default field values
gender: 1
}),
submitAll: true,
partialErrorChecking: true
});
this.form.addChangeListener(this.onFormChange);
}
// ...
Here, we’ve called this.form.init
and this.form.addChangeListener
.
addChangeListener
adds a listener for update
event.
init
initializes a form. It takes in one argument - an object with settings.
The fields
property defines form fields.
The model
property defines the form model. To create the form model, we’ve called UIKernel.Adapters.Grid.ToFormCreate
with grid model and an object of default field values as arguments.
submitAll: true
means that all form fields will be validated.
partialErrorChecking: true
means that the form fields will be validated in response to user input.
Note that the form uses the validation rules we’ve specified before.
// ...
componentWillUnmount() {
this.form.removeChangeListener(this.onFormChange);
}
onFormChange(newFormState) {
this.setState(newFormState);
}
// ...
Inside componentWillUnmount
we’ve removed an event listener by calling this.form.removeChangeListener
.
The onFormChange
method is called when form data changes.
// ...
handleSubmit(e) {
e.preventDefault();
this.form.submit() // create a new record
.then((recordId) => {
this.props.onSubmit(recordId)
})
.catch((err) => {
console.log(err);
});
}
// ...
handleSubmit
calls the this.form.submit
method which sends data to our model.
On successful submission, the callback from MainComponent
is invoked.
// ...
render() {
if (!this.state.isLoaded) {
return <span>Loading...</span>;
}
return (
<div>
<form className="form-horizontal edit-form" onSubmit={this.handleSubmit}>
<div className={"form-group" + (this.state.fields.name.isChanged ? ' changed' : '') +
(this.state.fields.name.errors ? ' error' : '')}>
<label className="col-sm-3 control-label">First Name</label>
<div className="col-sm-9">
<input
type="text"
placeholder="Alyx"
className="form-control"
onChange={this.form.updateField.bind(this.form, 'name')}
onFocus={this.form.clearError.bind(this.form, 'name')}
onBlur={this.form.validateForm}
value={this.state.fields.name.value}
/>
{this.state.fields.name.errors &&
<small className="control-label">{this.state.fields.name.errors[0]}</small>}
</div>
</div>
<div
className={"form-group" + (this.state.fields.surname.isChanged ? ' changed' : '') +
(this.state.fields.surname.errors ? ' error' : '')}>
<label className="col-sm-3 control-label">Last Name</label>
<div className="col-sm-9">
<input
type="text"
placeholder="Vance"
className="form-control"
onChange={this.form.updateField.bind(this.form, 'surname')}
onFocus={this.form.clearError.bind(this.form, 'surname')}
onBlur={this.form.validateForm}
value={this.state.fields.surname.value}
/>
{this.state.fields.surname.errors &&
<small className="control-label">{this.state.fields.surname.errors[0]}</small>}
</div>
</div>
<div
className={"form-group" + (this.state.fields.phone.isChanged ? ' changed' : '') +
(this.state.fields.phone.errors ? ' error' : '')}>
<label className="col-sm-3 control-label">Phone</label>
<div className="col-sm-9">
<input
type="text"
placeholder="555-0100"
className="form-control"
onChange={this.form.updateField.bind(this.form, 'phone')}
onFocus={this.form.clearError.bind(this.form, 'phone')}
onBlur={this.form.validateForm}
value={this.state.fields.phone.value}
/>
{this.state.fields.phone.errors &&
<small className="control-label">{this.state.fields.phone.errors[0]}</small>}
</div>
</div>
<div
className={"form-group" + (this.state.fields.age.isChanged ? ' changed' : '') +
(this.state.fields.age.errors ? ' error' : '')}>
<label className="col-sm-3 control-label">Age</label>
<div className="col-sm-9">
<UIKernel.Editors.Number
placeholder="18"
className="form-control"
onChange={this.form.updateField.bind(this.form, 'age')}
onFocus={this.form.clearError.bind(this.form, 'age')}
onBlur={this.form.validateForm}
value={this.state.fields.age.value}
/>
{this.state.fields.age.errors &&
<small className="control-label">{this.state.fields.age.errors[0]}</small>}
</div>
</div>
<div
className={"form-group" + (this.state.fields.gender.isChanged ? ' changed' : '')}>
<label className="col-sm-3 control-label">Gender</label>
<div className="col-sm-9">
<UIKernel.Editors.Select
options={[
[1, 'Male'],
[2, 'Female']
]}
className="form-control"
onChange={this.form.updateField.bind(this.form, 'gender')}
onFocus={this.form.clearError.bind(this.form, 'gender')}
onBlur={this.form.validateForm}
value={this.state.fields.gender.value}
/>
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-3 col-sm-9">
<button type="button" className="btn btn-success" onClick={this.form.clearChanges}>
Clear
</button>
{' '}
<button type="submit" className="btn btn-primary">
Add
</button>
</div>
</div>
</form>
</div>
);
}
All inputs have the onChange
, onFocus
, and onBlur
props with callbacks set.
this.form.updateField
updates the field value.
this.form.clearError
clears the field error mark.
this.form.validateForm
validates the form.
Using the ternary operator, we dynamically add classes to our elements. The ternary operator allows us to specify two different classes, one if a function returns true and one for false.
this.state.fields['<field-name>'].errors
holds an array of validation errors for the form field ‘<field-name>’.
this.state.fields['<field-name>'].errors === null
if there is no errors in the form field ‘<field-name>’.
this.state.fields['<field-name>'].isChanged
indicates if the form field ‘<field-name>’ has been changed.
Now let’s open the main.css
file and add there the following code:
.edit-form .changed {
background-color: #ffff38;
}
.edit-form .error {
background-color: #ff8689;
}
Next up, let’s modify MainComponent
.
MainComponent.js
:
highlightNewRecord(recordId) {
this.grid.addRecordStatus(recordId, 'new'); // mark the record as new
}
// ...
render() {
return (
<div>
<div className="row">
<div className="col-sm-8">
<div className="panel panel-primary">
<div className="panel-heading">
<h3 className="panel-title">Add record</h3>
</div>
<div className="panel-body">
<CreateForm
onSubmit={(recordId) => this.highlightNewRecord(recordId)}
/>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="panel panel-primary">
<div className="panel-heading">
<h3 className="panel-title">Filters</h3>
</div>
<div className="panel-body">
<FiltersForm filters={this.state.filters}
onChange={(filters) => this.onFiltersChange(filters)}
onClear={() => this.onFiltersChange(DEFAULT_FILTERS)}/>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="panel panel-info">
<div className="panel-heading">
<h3 className="panel-title">Records</h3>
</div>
<UIKernel.Grid
ref={(grid) => this.grid = grid}
model={this.state.model} // Grid model
cols={columns} // columns configuration
viewCount={10} // display 10 records per page
defaultSort= // default sorting
/>
<div className="panel-footer">
<a className="btn btn-success" onClick={() => this.clearChanges()}>Clear</a>
{' '}
<a className="btn btn-primary" onClick={() => this.saveChanges()}>Save</a>
</div>
</div>
</div>
</div>
</div>
);
}
Here, we’ve defined the highlightNewRecord
and updateCreateFormState
methods and changed the render
method.
highlightNewRecord
is called after successful submitting of CreateForm
. It highlights new records.
updateCreateFormState
is called when CreateForm
data changes. It updates this.state.createFormState
.
Finally, let’s modify the model.js
file:
const model = new UIKernel.Models.Grid.Collection({
// ...
requiredFields: ["name", "surname", "phone", "age", "gender"]
});
Using UIKernel, we’ve built an editable grid which has sorting, filtering and pagination without too much work. The capabilities of UIKernel go beyond what we’ve seen here. Check out more examples here.