A Ruby on Rails inspired model validation framework that is completely and utterly computed property based.
No observers were used nor harmed while developing and testing this addon.
- Lazily computed validations
- Ruby on rails inspired validators
- Support for both Ember Data Models and Objects
- Synchronous and asynchronous support for both validators and validations
- Dirty tracking
- Support for nested models via
belongsToandhasManyrelationships - Support for nested objects
- Easily integrated with Ember Data's DS.Errors
- No observers. Seriously... there are none. Like absolutely zero....
- Meta data based cycle tracking to detect cycles within your model relationships which could break the CP chain
- Custom validators
- Ember CLI generator to create custom validators with a unit test
- Debounceable validations
- Warning validations
- I18n support
ember install ember-cp-validationsIf you are upgrading from 2.x to 3.x, please checkout the upgrading documentation;
If it is a bug please open an issue on GitHub.
The first thing we need to do it build our validation rules. This will then generate a Mixin that you will be able to incorporate into your model or object.
// models/user.js
import Ember from 'ember';
import DS from 'ember-data';
import {
validator, buildValidations
}
from 'ember-cp-validations';
const Validations = buildValidations({
username: validator('presence', true),
password: [
validator('presence', true),
validator('length', {
min: 4,
max: 8
})
],
email: [
validator('presence', true),
validator('format', { type: 'email' })
],
emailConfirmation: [
validator('presence', true),
validator('confirmation', {
on: 'email',
message: '{description} do not match',
description: 'Email addresses'
})
]
});Once our rules are created and our Mixin is generated, all we have to do is add it to our model.
// models/user.js
export default DS.Model.extend(Validations, {
'username': attr('string'),
'password': attr('string'),
'email': attr('string')
});You can also use the generated Validations mixin on any Ember.Object or child
of Ember.Object, like Ember.Component. For example:
// components/x-foo.js
import Ember from 'ember';
import {
validator, buildValidations
}
from 'ember-cp-validations';
const Validations = buildValidations({
bar: validator('presence', true)
});
export default Ember.Component.extend(Validations, {
bar: null
});To lookup validators, container access is required which can cause an issue with Ember.Object creation if the object is statically imported. The current fix for this is as follows.
// models/user.js
export default Ember.Object.extend(Validations, {
username: null
});Ember < 2.3.0-beta.1
// routes/index.js
import User from '../models/user';
export default Ember.Route.extend({
model() {
var container = this.get('container');
return User.create({ username: 'John', container })
}
});Ember >= 2.3.0-beta.2
// routes/index.js
import Ember from 'ember';
const { getOwner } = Ember;
import User from '../models/user';
export default Ember.Route.extend({
model() {
return User.create(
getOwner(this).ownerInjection(),
{ username: 'John' }
);
}
});Default options can be specified over a set of validations for a given attribute. Local properties will always take precedence.
Instead of doing the following:
const Validations = buildValidations({
username: [
validator('presence', {
presence: true,
description: 'Username'
}),
validator('length', {
min: 1,
description: 'Username'
}),
validator('my-custom-validator', {
description: 'A username'
})
]
});We can declare default options:
const Validations = buildValidations({
username: {
description: 'Username'
validators: [
validator('presence', true),
validator('length', {
min: 1
}),
validator('my-custom-validator', {
description: 'A username'
})
]
},
});In the above example, all the validators for username will have a description of Username except that of the my-custom-validator validator which will be A username.
All options can also be Computed Properties. These CPs have access to the model and attribute that is associated with the validator.
Please note that the message option of a validator can also be a function with the following signature.
const Validations = buildValidations({
username: validator('length', {
disabled: Ember.computed.not('model.meta.username.isEnabled'),
min: Ember.computed.readOnly('model.meta.username.minLength'),
max: Ember.computed.readOnly('model.meta.username.maxLength'),
description: Ember.computed(function() {
// CPs have access to the `model` and `attribute`
return this.get('model').generateDescription(this.get('attribute'));
}).volatile() // Disable caching and force recompute on every get call
})
});When declaring object validations (not including Ember Data models), it is possible to validate child objects from the parent object.
import Ember from 'ember';
import { validator, buildValidations } from 'ember-cp-validations';
const Validations = buildValidations({
'acceptTerms': validator('inclusion', { in: [ true ] }),
'user.firstName': validator('presence', true),
'user.lastName': validator('presence', true),
'user.account.number': validator('number')
});
export default Ember.Component.extend(Validations, {
acceptTerms: false,
user: {
firstName: 'John',
lastName: 'Doe' ,
account: {
number: 123456,
}
},
isFormValid: Ember.computed.alias('validations.isValid'),
});