Add validations to your Ember Data models on an explicit and easy way, without a bunch a validations files around or complicated structure.
This README outlines the details of collaborating on this Ember addon.
This Ember addon was born from the necessity of having a validation support for models similar to Active Record Validations on the Ruby on Rails land.
Install Ember-model-validator is easy as:
npm install ember-model-validator --save-dev
or
yarn add ember-model-validator --dev
Ember-model-validator provides a decorator to be included in your models for adding validation support. This decorator can be imported from your app's namespace (e.g. import { modelValidator, objectValidator } from 'ember-model-validator';
in your models).
By including Ember-model-validator's decorator into your model, this will have a validate
function available, it is a synchronous function which returns either true or false.
You can also pass an option hash for excluding or forcing certain attributes to be validated, and to prevent errors to be added.
// Using `except`
myModel.validate({ except: ['name', 'cellphone'] });
// Using `only`
myModel.validate({ only: ['favoriteColor', 'mainstreamCode'] });
// Using `addErrors`
myModel.validate({ addErrors: false });
// ^ This will validate the model but won't add any errors.
To target specific validations when using except
/only
, pass the validations' names along the attribute's name:
// This runs all validations, except name's presence and length validations and
// any email validations.
// Other name validations are still run.
myModel.validate({ except: ['name:presence,length', 'email'] });
import Model, { attr } from '@ember-data/model';
import { modelValidator } from 'ember-model-validator';
@modelValidator
export default class MyModel extends Model {
@attr('string') fullName;
@attr('string') fruit;
@attr('string') favoriteColor;
validations = {
fullName: {
presence: true
},
fruit: {
presence: true
},
favoriteColor: {
color: true
}
};
}
After setting the validations on your model you will be able to:
import Controller from '@ember/controller';
import { action } from '@ember/object';
export default class MyController extends Controller {
@action
async saveFakeModel() {
const fakeModel = this.model;
if (fakeModel.validate()) {
await fakeModel.save();
} else {
console.log({ errors: fakeModel.get('errors') });
}
}
}
import Component from '@ember/component';
import { objectValidator } from 'ember-model-validator';
@objectValidator
export default class MyComponent extends Component {
test = 'ABC',
validations = {
test: {
presence: true
}
}
};
import Model, { attr } from '@ember-data/model';
import { modelValidator, type ValidationsConfig, type ValidatedModel } from 'ember-model-validator';
// https://github.com/microsoft/TypeScript/issues/4881
interface MyModel extends ValidatedModel, Model {}
@modelValidator
class MyModel extends Model {
@attr('string') declare name: string;
validations: ValidationsConfig = {
name: {
presence: true,
},
email: {
presence: true,
email: true,
},
};
}
export default MyModel;
declare module 'ember-data/types/registries/model' {
export default interface ModelRegistry {
'my-model': MyModel;
}
}
ember-source
andember-data
v3.28 or above
- Ember model validator
All validators accept the following options
message
option. Overwrites the default message, it can be a String (with a{value}
in it for value interpolation) or a function that returns a string.errorAs
option. Sets the key name to be used when adding errors (default to property name).allowBlank
option. If set totrue
and the value is blank as defined by Ember.isBlank, all other validations for the field are skipped.if
option. Validates only when the function passed returns true.function(key,value, _this){...}
.
A value is not present if it is empty or a whitespace string. It uses Ember.isBlank method. This can be also used on async belongsTo
relations.
validations = {
name: {
presence: true;
}
}
These values: ['1', 1, true]
are the acceptable values. But you can specify yours with the accept
option.
validations = {
acceptConditions: {
acceptance: {
accept: 'yes';
}
}
}
The
accept
option receives either a string or an array of acceptable values.
Validates that the specified attributes are absent. It uses Ember.isPresent method.
validations = {
login: {
absence: true;
}
}
Specify a Regex to validate with. It uses the match() method from String.
validations = {
legacyCode:{
format: { with: /^[a-zA-Z]+$/ }
}
}
Specify the lengths that are allowed.
- A
number
. The exact length of the value allowed (Alias foris
). - An
array
. Will expand tominimum
andmaximum
. First element is the lower bound, second element is the upper bound. is
option. The exact length of the value allowed.minimum
option. The minimum length of the value allowed.maximum
option. The maximum length of the value allowed.
validations = {
socialSecurity: {
length: 5
},
nsaNumber: {
length: [3, 5]
},
chuncaluchoNumber: {
length: { is: 10, message: 'this is not the length of a chuncalucho' }
},
hugeName:{
length: {
minimum: 3,
maximum: 5
}
},
smallName:{
length: {
maximum: {
value: 3,
message: 'should be smaller'
}
}
}
}
Validates the proper format of the email.
validations = {
email: {
email: true
}
}
The value must be a correct zipcode. The countryCode
is optional and defaults to 'US'.
Countries supported and regular expressions used can be found in postal-codes-regex.js
countryCode
option. The code of the country for which the postal code will be validated.
validations = {
postalCode: {
// If no countryCode is specified, 'US' is used as default
zipCode: true;
}
}
validations = {
postalCodeUK: {
zipCode: {
countryCode: 'UK';
}
}
}
The value must be a correct Hexadecimal color.
validations = {
favoriteColor: {
color: true;
}
}
The value must a well formatted subdomain. Here you can also specify reserved words.
validations = {
mySubdomain: {
subdomain: {
reserved: ['admin', 'blog'];
}
}
}
The value must a well formatted URL.
validations = {
myBlog: {
URL: true;
}
}
The value has to be included in a given set.
validations = {
name:{
inclusion: { in: ['Jose Rene', 'Aristi Gol', 'Armani'] }
}
}
The value can't be included in a given set.
validations = {
name:{
exclusion: { in: ['Gionvany Hernandez', 'Wilder Medina'] }
}
}
Specify the attribute to match with.
- A
string
. The name of the attribute to match with (Alias forattr
). attr
option. The name of the attribute to match with.
validations = {
email:{
match: 'confirmationEmail'
},
password:{
match: {
attr: 'passwordConfirmation',
message: 'sup, it is not the same!'
}
}
}
The value has to have only numeric values.
onlyInteger
option. The value must be an integer.greaterThan
option. The value must be greater than the supplied value.greaterThanOrEqualTo
option. The value must be greater or equal to the supplied value.equalTo
option. The value must be equal to the supplied value.lessThan
option. The value must be less than the supplied value.lessThanOrEqualTo
option. The value must be less or equal to the supplied value.odd
option. The value must be odd.even
option. The value must be even.
validations = {
lotteryNumber: {
numericality: true;
}
}
The value must be a Date
object or a string that produces a valid date when passed to the Date
constructor.
before
option. The value must be before the supplied date.after
option. The value must be after the supplied date.
validations = {
birthDate: {
date: {
before: new Date()
}
},
signupDate: {
date: {
after: '2015-01-01'
}
}
}
Define a custom callback function to validate the model's value. The validation callback is passed 3 values: the key, value, model's scope. return true (or a truthy value) to pass the validation, return false (or falsy value) to fail the validation.
validations = {
lotteryNumber: {
custom: function(key, value, model){
return model.get('accountBalance') > 1 ? true : false;
}
}
}
this has the same action as above except will use a custom message instead of the default.
validations = {
lotteryNumber: {
custom: {
validation: function(key, value, model){
return model.get('accountBalance') > 1 ? true : false;
},
message: 'You can\'t win off of good looks and charm.'
}
}
}
to have multiple custom validation functions on the same property, give 'custom' an array of either of the two syntax above.
validations = {
lotteryNumber: {
custom: [
{
validation: function(key, value, model) {
return model.get('accountBalance') > 1 ? true : false;
},
message: "You can't win off of good looks and charm."
},
{
validation: function(key, value, model) {
return model.get('accountBalance') > 1 ? true : false;
},
message: "You can't win off of good looks and charm."
}
];
}
}
A set of validators which are especially useful for validating passwords. Be aware that these all of these password-aimed validations will work standalone and carry the same common options with the rest of the validations. They don't only work for passwords!
mustContainCapital
(capital case character).mustContainLower
(lower case character).mustContainNumber
mustContainSpecial
length
(explained in-depth above).
validations = {
password: {
presence: true,
mustContainCapital: true,
mustContainLower: true,
mustContainNumber: true,
mustContainSpecial: {
message: 'One of these characters is required: %@',
acceptableChars: '-+_!@#$%^&*.,?()'
},
length: {
minimum: 6
}
},
someOtherThing: {
mustContainSpecial: true
}
}
This validator will run the validate()
function for the specific relation. If it's a DS.hasMany
relation then it will loop through all objects.
Note: The relations have to be
embedded
or the promise has to be already resolved.
validations = {
myHasManyRelation:{
relations: ['hasMany']
},
myBelongsToRelation:{
relations: ['belongsTo']
}
}
You can pass a function to generate a more specific error message. Some scenarios are:
- When the message varies depending of the attribute value.
- When you want to use model attributes in the message.
The message function receives the attribute name, the value of the attribute and the model itself.
NOTE: If the function doesn't return a string the default message is going to be used.
import Model, { attr } from '@ember-data/model';
import { modelValidator } from 'ember-model-validator';
@modelValidator
export default class MyModel extends Model {
@attr('number', { defaultValue: 12345 }) otherCustomAttribute;
validations = {
otherCustomAttribute: {
custom: {
validation: function(key, value) {
return value.toString().length === 5 ? true : false;
},
message: function(key, value, _this) {
return key + ' must have exactly 5 digits';
}
}
}
};
}
Set validatorDefaultLocale
in your config enviroment a language, for now it's possible use 'en', 'fr', 'es', 'uk', 'hu', 'sr', 'sr-cyrl' or 'pt-br', default is 'en';
//config/environment.js
...
ENV:{
...
APP:{
validatorDefaultLocale: 'pt-br'
}
...
}
...
npm test
(Runsember try:each
to test your addon against multiple Ember versions)ember test
ember test --server
See the Contributing guide for details.