Creating records

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"]
});

Conclusion

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.