Ember addon for adding validation functionality to your Ember Data Models.
- Ember.js v3.12 or above
- Ember CLI v3.12 or above
- Node.js v10 or above
To add validation to your Models you must use the ValidatorMixin
provided by the addon and define validation
options to your Attributes.
import DS from 'ember-data';
import ValidatorMixin from "@getflights/ember-attribute-validations/mixins/validator';
export DS.Model.extend(ValidatorMixin, {
name: DS.atrt('string', {
validation: {
required: true,
min: 5
}
}),
email: DS.atrt('string', {
validation: {
required: true,
email: true,
max: 254
}
})
age: DS.atrt('number', {
validation: {
digit: true,
range: {
from: 12,
to: 99
}
}
}),
website: DS.atrt('string', {
validation: {
url: true
}
}),
newsletter: DS.atrt('boolean', {
validation: {
acceptance: true
}
})
});
Validation of the Model is executed in the save
method of the Model. If the validation fails, promise is rejected with ValidationError
that contains the DS.Errors
object.
You can also manually call the validate
method directly on the Model, if the Model is valid, this would return true
otherwise false
.
This addon comes with couple of built in Validators
:
- RequiredValidator
- AcceptanceValidator
- DigitValidator
- EmailValidator
- InValidator (EnumValidator)
- MaxValidator
- MinValidator
- NumberValidator
- RangeValidator
- URLValidator
- PatternValidator
- DateValidator
- DateBeforeValidator
- DateAfterValidator
- UUIDValidator
- DecimalsValidator
- DigitValidator
- WholeNumberValidator
- PrecisionValidator
You can also create your own custom Validators
by using the provided blueprint.
ember generate validator validator-name
And you would get something like this:
import Validator from '@getflights/ember-attribute-validations/validator';
export default Validator.extend({
message: 'something is wrong with %@',
validate(name, value, attribute, model) {
if(this.get('optionForYourValidator' && name) {
// this would format the Error message
return this.format();
}
}
});
And use your validator like so:
import DS from 'ember-data';
import ValidatorMixin from '@getflights/ember-attribute-validations/mixins/validator';
export DS.Model.extend(ValidatorMixin, {
validation: {
validatorName: {
optionForYourValidator: false
}
}
});
As you may have noticed we are relying on a simple naming convention to run validation. In this example, we have defined additional property for our Validator
instance. You can also pass a message
property that would have precedence over the existing message
property defined in the ValidatorNameValidator
.
or if you wish to create a regex based Validator
:
ember generate pattern-validator pattern-validator-name
import PatternValidator from "@getflights/ember-attribute-validations/pattern-validator";
export default PatternValidator.extend({
message: "%@ is not passing the test",
pattern: /my pattern/,
});
import DS from 'ember-data';
import ValidatorMixin from '@getflights/ember-attribute-validations/mixins/validator';
export DS.Model.extend(ValidatorMixin, {
validation: {
patternValidatorName: true
}
});
You can also do this manualy, but keep in mind that all validators must be either placed in the validators
folder or be directly registered in the Ember container with the type validator
.
This addon comes with a MessageResolver
that resolves an error message for a failed attribute validation. By default, this addon uses built in validation error messages. You may wish to change them and your own error messages for your application.
The simplest way to do that is by overiding the resolveMessage
method in the MessageResolver
. You can place your implementation in the resolvers
folder, this way it would be automatically picked up by the Ember Resolver. You can register the Resolver in the container manually with key resolver:validation-message
or just simply reopen the class.
When a validation of the attribute fails, Validator
creates an error message by invoking the MessageResolver.resolve
method. This method will create an object that contains couple of message keys. These keys are used to locate the error message from the configured message catalog.
For instance if we have a Model with 2 attributes, declared like this in your models/user.js
file:
import DS from "ember-data";
export default DS.Model.extend({
name: DS.attr("string", {
validation: {
required: true,
},
}),
age: DS.attr("number", {
validation: {
required: true,
min: 18,
},
}),
});
And we have created our own implementation of the MessageResolver
that we placed in the resolvers/message-resolver.js
file. Here we are going to declare our own catalog of error messages.
import MessageResolver from "@getflights/ember-attribute-validations/message-resolver";
const { computed, get } = Ember;
export default MessageResolver.extend({
catalog: computed(function () {
return {
required: "Field %@ is required",
"min.string": "String must have more than %@ characters",
user: {
age: {
min: "You must have more than 18 years to register.",
},
},
};
}),
resolveMessage(key) {
var catalog = this.get("catalog");
return get(catalog, key);
},
});
When the MessageResolver.resolve
method is invoked for the name
attribute from the required
validator, Resolver would create this set of keys for which it will search the catalog:
{
attributeType: 'string',
validatorType: 'required',
modelType: 'user',
validatorPath: 'required.string',
modelPath: 'user.name.required'
}
Once the keys are generated, the MessageResolver.resolve
method would first invoke the resolveMessage
method with the modelPath
value. If the message does not exists, it would try again to find a message with a validatorPath
value. If the message is still not resolved, it tries again with the validatorType
value or it would throw an Assertion Error.
So when the validation fails for the name
attribute with required
Validator, the resolveMessage
would be invoked with the modelPath
value, which is user.name.required
. This key does not exists in our catalog, so the resolver will try again with the validatorPath
value, which is required.string
. This will again fail to resolve the message, as this key does not exists. Next iteration would be with the validatorType
value. This key exists, and the Field %@ is required
would be returned.
In the case when the min
Validator fails for the age
attribute, MessageResolver
would generate these keys:
{
attributeType: 'number',
validatorType: 'age',
modelType: 'user',
validatorPath: 'min.number',
modelPath: 'user.age.min'
}
The error message for this case, would be resolved to You must have more than 18 years to register
.
Once the message is found, it is stored in the cache for the current looked up key.
MessageResolver
also resolves the message for the ValidationError
that is thrown when the validation fails when calling DS.Model.save
method. The key used for this message is error
.
You can also register the messages in the container, and they can be fetched from there and used inside the MessageResolver
.
import MessageResolver from "@getflights/ember-attribute-validations/message-resolver";
const { computed, get } = Ember;
export default MessageResolver.extend({
catalog: computed(function () {
return this.container.lookup("i18n:messages");
}),
resolveMessage(key) {
var catalog = this.get("catalog");
return get(catalog, key);
},
});
Examples on how to use validators.
import DS from "ember-data";
export default DS.Model.extend({
name: DS.attr("string", {
validation: {
required: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
published: DS.attr("boolean", {
validation: {
acceptance: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
age: DS.attr("number", {
validation: {
decimals: 2,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
age: DS.attr("number", {
validation: {
digit: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
dateOfBirth: DS.attr("date", {
validation: {
date: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
dateOfBirth: DS.attr("date", {
validation: {
before: new Date(),
},
}),
});
Or with a function
import moment from "moment";
import DS from "ember-data";
export default DS.Model.extend({
dateOfBirth: DS.attr("date", {
validation: {
before() {
return moment().startOf("d").toDate();
},
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
dateOfBirth: DS.attr("date", {
validation: {
after: new Date(),
},
}),
});
Or with a function
import moment from "moment";
import DS from "ember-data";
export default DS.Model.extend({
dateOfBirth: DS.attr("date", {
validation: {
after() {
return moment().startOf("d").toDate();
},
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
email: DS.attr("string", {
validation: {
email: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
property: DS.attr("string", {
validation: {
in: ["foo", "bar"],
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
age: DS.attr("number", {
validation: {
max: 18,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
age: DS.attr("number", {
validation: {
min: 18,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
age: DS.attr("number", {
validation: {
number: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
age: DS.attr("number", {
validation: {
precision: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
age: DS.attr("number", {
validation: {
range: {
from: 0,
to: 18,
},
},
}),
});
Or with a String length
import DS from "ember-data";
export default DS.Model.extend({
password: DS.attr("string", {
validation: {
range: {
from: 8,
to: 12,
},
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
website: DS.attr("string", {
validation: {
url: true,
},
}),
});
Here you can pass all
, 3
, 4
or 5
as a version
property value. If all
is passed it validates the 1 and 2 UUID verions.
import DS from "ember-data";
export default DS.Model.extend({
uuid: DS.attr("string", {
validation: {
uuid: {
version: 3,
},
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
uuid: DS.attr("string", {
validation: {
wholenumber: true,
},
}),
});
import DS from "ember-data";
export default DS.Model.extend({
pattern: DS.attr("string", {
validation: {
pattern: "some regex rule",
},
}),
});
More documentation to follow...
ember server
- Visit your app at http://localhost:4200.
ember test
ember test --server
ember build
For more information on using ember-cli, visit http://www.ember-cli.com/.