ember-validated-form-buffer icon indicating copy to clipboard operation
ember-validated-form-buffer copied to clipboard

A validated form buffer that wraps Ember Data models for use in forms.

Build Status

ember-validated-form-buffer

ember-validated-form-buffer implements a validating buffer that wraps Ember Data models and can be used in forms to buffer user inputs before applying them to the underlying model. The buffer also handles mixing client side validation errors and errors returned from the API as well as functionality that detects which API errors may have become obsolete due to modifications to the respective properties.

ember-validated-form-buffer helps implementing common forms functionality:

  • preventing modification of models until the form is submitted
  • implementing cancel/reset functionality
  • filtering irrelevant errors

It leverages ember-buffered-proxy for the buffering functionality and ember-cp-validations for client side validations.

Installation

Install ember-validated-form-buffer with

ember install ember-validated-form-buffer

Example

In order to define a validated form buffer on a controller or component, import the formBufferProperty helper and define a property that wraps the model instance. Pass in the validations mixin as returned by ember-cp-validations. When the form is submitted, apply the buffered changes and save the model or discard them to reset all user input:

import Ember from 'ember';
import { validator, buildValidations } from 'ember-cp-validations';
import formBufferProperty from 'ember-validated-form-buffer';

const Validations = buildValidations({
  name: validator('presence', true)
});

export default Ember.Controller.extend({
  data: formBufferProperty('model', Validations),

  actions: {
    submit(e) {
      e.preventDefault();

      this.get('data').applyBufferedChanges();
      this.get('model').save();
    },

    reset() {
      this.get('data').discardBufferedChanges();
    }
  }
});

Then instead of binding form inputs to model properties directly, bind them to the buffer instead:

<form onsubmit={{action 'submit'}}>
  <label>Name</label>
  {{input value=data.name}}
  <button type="submit">Save</button>
  <button type="button" onclick={{action 'reset'}}>Reset</button>
</form>

If you're not using 2 way data bindings for the input but Data Down/Actions Up, make sure to update the buffer property instead of the model's when the respective action is called:

<form onsubmit={{action 'submit'}}>
  <label>Name</label>
  <input value="{{data.name}}" onkeydown={{action (mut data.name) value='currentTarget.value'}}/>
  <button type="submit">Save</button>
  <button type="button" onclick={{action 'reset'}}>Reset</button>
</form>

API

The buffer

The buffer has methods for applying and discarding changes as well as properties for accessing its current error state.

  • applyBufferedChanges applies the changes in the buffer to the underlying model.

  • discardBufferedChanges discards the buffered changes to that the buffer's state is reset to that of the underlying model.

  • apiErrors returns the errors as returned by the API when the model was last submitted.

  • clientErrors returns the client side validation errors as returned by ember-cp-validations.

  • displayErrors returns both the API errors as well as the client side validation errors. This does not include any API errors on properties that have been changed after the model was submitted as changing a property that was previously rejected by the API potentially renders the respective error invalid.

  • hasDisplayErrors returns whether the buffer currently has any errors to display which is the case when displayErrors is not empty.

For further info on the buffer's API, check the docs of ember-buffered-proxy and ember-cp-validations respectively.

The buffer can be imported and used directly:

import { Buffer } from 'ember-validated-form-buffer';

const Validations = buildValidations({
  name: validator('presence', true)
});

export default Ember.Controller.extend({
  data: computed('model', function() {
    let owner = Ember.getOwner(this);
    return Buffer.extend(Validations).create(owner.ownerInjection(), {
      content: this.get('model')
    });
  }),

…

It is generally easier to use the formBufferProperty macro to define a form buffer property though:

The formBufferProperty helper

The formBufferProperty macro takes the name of another property that returns the Ember Data model to wrap in the buffer as well as a list of mixins that will be applied to the buffer. These mixins usually include the validation mixin as created by ember-cp-validations's buildValidations method.

If any of the provided mixins define an unsetApiErrors method, that method will be called whenever any property is changed on the buffer. The method returns a property name or an array of property names for which all API errors will be excluded from the displayErrors until the model is submitted to the API again. That way it's possible to hide API errors on a property when a related property changes:

import formBufferProperty from 'ember-validated-form-buffer';

const Validations = buildValidations({
  name: validator('presence', true)
});

export default Ember.Controller.extend({
  data: formBufferProperty('model', Validations, {
    unsetApiErrors() {
      let changedKeys = Ember.A(Object.keys(this.get('buffer')));
      if (changedKeys.includes('date') || changedKeys.includes('time')) {
        return 'datetime'; // whenever the "date" or "time" attributes change, also hide errors on the virtual "datetime" property
      }
    }
  })

…

License

ember-validated-form-buffer is developed by and © simplabs GmbH and contributors. It is released under the MIT License.

ember-validated-form-buffer is not an official part of Ember.js and is not maintained by the Ember.js Core Team.