• This repository has been archived on 31/Jan/2019
  • Stars
    star
    832
  • Rank 54,811 (Top 2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 11 years ago
  • Updated about 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Validations for Ember Objects

Ember Validations

Build Status CircleCI npm version Ember Observer Score

ember-validations is built and maintained by DockYard, contact us for expert Ember.js consulting.

WARNING

This addon is no longer actively developed. At DockYard we have switched over to using ember-changeset-validations together with ember-changeset.

We do still maintain this addon.

Installing

ember install ember-validations

Looking for help?

If it is a bug please open an issue on GitHub.

Usage

You need to mixin EmberValidations into any Ember.Object you want to add validations to:

import Ember from 'ember';
import { Mixin } from 'ember-validations';

export default Ember.Controller.extend(Mixin);

You define your validations as a JSON object. They should be added to the controller that represents the model in question. The keys in the object should map to properties. If you pass a JSON object as the value this will be seen as validation rules to apply to the property. If you pass true then the property itself will be seen as a validatable object.

import Ember from 'ember';
import { Mixin } from 'ember-validations';

export default Ember.Controller.extend(Mixin, {
  validations: {
    'model.firstName': {
      presence: true,
      length: { minimum: 5 }
    },
    'model.age': {
      numericality: true
    },
    'model.profile': true
  }
});

Though not yet explicitly part of the API, you can also add validators to nested objects:

import Ember from 'ember';
import { Mixin } from 'ember-validations';

export default Ember.Component.extend(Mixin, {
  validations: {
    'user.firstName': {
      presence: true,
      length: { minimum: 5 }
    }
  }
});

This is useful for things like Components which don't act as proxies, but again, until this is officially built into the project, YMMV.

Note: If you override the init function, you must call _super()

import Ember from 'ember';
import { Mixin } from 'ember-validations';

export default Ember.Controller.extend(Mixin, {
  init: function() {
    // this call is necessary, don't forget it!
    this._super.apply(this, arguments);

    // Your init code...
  }
});

Validators

Absence

Validates the property has a value that is null, undefined, or ''

Options

  • true - Passing just true will activate validation and use default message
  • message - Any string you wish to be the error message. Overrides i18n.
// Examples
absence: true
absence: { message: 'must be blank' }

Acceptance

By default the values '1', 1, and true are the acceptable values

Options

  • true - Passing just true will activate validation and use default message
  • message - Any string you wish to be the error message. Overrides i18n.
  • accept - the value for acceptance
// Examples
acceptance: true
acceptance: { message: 'you must accept', accept: 'yes' }

Confirmation

Expects a propertyConfirmation to have the same value as property. The validation must be applied to the property, not the propertyConfirmation (otherwise it would expect a propertyConfirmationConfirmation).

Options

  • true - Passing just true will activate validation and use default message
  • message - Any string you wish to be the error message. Overrides i18n.
// Examples
confirmation: true
confirmation: { message: 'you must confirm' }

Exclusion

A list of values that are not allowed

Options

  • message - Any string you wish to be the error message. Overrides i18n.
  • allowBlank - If true skips validation if value is empty
  • in - An array of values that are excluded
  • range - an array with the first element as the lower bound the and second element as the upper bound. Any value that falls within the range will be considered excluded
// Examples
exclusion: { in: ['Yellow', 'Black', 'Red'] }
exclusion: { range: [5, 10], allowBlank: true, message: 'cannot be between 5 and 10' }

Format

A regular expression to test with the value

Options

  • message - Any string you wish to be the error message. Overrides i18n.
  • allowBlank - If true skips validation if value is empty
  • with - The regular expression to test with
// Examples
format: { with: /^([a-zA-Z]|\d)+$/, allowBlank: true, message: 'must be letters and numbers only'  }

Inclusion

A list of the only values allowed

Options

  • message - Any string you wish to be the error message. Overrides i18n.
  • allowBlank - If true skips validation if value is empty
  • in - An array of values that are allowed
  • range - an array with the first element as the lower bound the and second element as the upper bound. Only values that fall within the range will be considered allowed
// Examples
inclusion: { in: ['Yellow', 'Black', 'Red'] }
inclusion: { range: [5, 10], allowBlank: true, message: 'must be between 5 and 10' }

Length

Define the lengths that are allowed

Options

  • number - Alias for is
  • array - Will expand to minimum and maximum. First element is the lower bound, second element is the upper bound.
  • allowBlank - If true skips validation if value is empty
  • minimum - The minimum length of the value allowed
  • maximum - The maximum length of the value allowed
  • is - The exact length of the value allowed
  • tokenizer - A function that should return a object that responds to length
Messages
  • tooShort - the message used when the minimum validation fails. Overrides i18n
  • tooLong - the message used when the maximum validation fails. Overrides i18n
  • wrongLength - the message used when the is validation fails. Overrides i18n
// Examples
length: 5
length: [3, 5]
length: { is: 10, allowBlank: true }
length: { minimum: 3, maximum: 5, messages: { tooShort: 'should be more than 3 characters', tooLong: 'should be less than 5 characters' } }
length: { is: 5, tokenizer: function(value) { return value.split(''); } }

Numericality

Will ensure the value is a number

Options

  • true - Passing just true will activate validation and use default message
  • allowBlank - If true skips validation if value is empty
  • onlyInteger - Will only allow integers
  • greaterThan - Ensures the value is greater than
  • greaterThanOrEqualTo - Ensures the value is greater than or equal to
  • equalTo - Ensures the value is equal to
  • lessThan - Ensures the value is less than
  • lessThanOrEqualTo - Ensures the value is less than or equal to
  • odd - Ensures the value is odd
  • even - Ensures the value is even
Messages
  • numericality - Message used when value failes to be a number. Overrides i18n
  • onlyInteger - Message used when value failes to be an integer. Overrides i18n
  • greaterThan - Message used when value failes to be greater than. Overrides i18n
  • greaterThanOrEqualTo - Message used when value failes to be greater than or equal to. Overrides i18n
  • equalTo - Message used when value failes to be equal to. Overrides i18n
  • lessThan - Message used when value failes to be less than. Overrides i18n
  • lessThanOrEqualTo - Message used when value failes to be less than or equal to. Overrides i18n
  • odd - Message used when value failes to be odd. Overrides i18n
  • even - Message used when value failes to be even. Overrides i18n
// Examples
numericality: true
numericality: { messages: { numericality: 'must be a number' } }
numericality: { odd: true, messages: { odd: 'must be an odd number' } }
numericality: { onlyInteger: true, greaterThan: 5, lessThanOrEqualTo : 10 }

Presence

Validates the property has a value that is not null, undefined, or ''

Options

  • true - Passing just true will activate validation and use default message
  • message - Any string you wish to be the error message. Overrides i18n.
// Examples
presence: true
presence: { message: 'must not be blank' }

Uniqueness

Not yet implemented.

Conditional Validators

Each validator can take an if or an unless in its options hash. The value of the conditional can be an inline function, a string that represents a property on the object, or a string that represents a function on the object. The result should be a boolean.

note that if is considered a keyword in IE8 and so you should put it in quotes

// function form
'model.firstName': {
  presence: {
    'if': function(object, validator) {
      return true;
    }
  }
}

// string form
// if 'canValidate' is a function on the object it will be called
// if 'canValidate' is a property object.get('canValidate') will be called
'model.firstName': {
  presence: {
    unless: 'canValidate'
  }
}

Custom Validators

With Ember-CLI

You can place your custom validators into my-app/app/validators/{local,remote}/<name>:

import Base from 'ember-validations/validators/base';

export default Base.extend({
  // ...
});

It is recommended that you separate between local and remote validators. However, if you wish you can place your validator into my-app/app/validators/<name>. However, any similarly named validator in local/ or remote/ has a higher lookup presedence over those in validators/.

The "native" validators that come with ember-validations have the lowest lookup priority.

Without Ember-CLI

You can add your validators to the global object:

EmberValidations.validators.local.<ClassName> =
EmberValidations.validators.Base.extend({
  // ...
});

Creating

To create a new validator you need to override the call function. When the validator is run its call function is what handles determining if the validator is valid or not. Call has access to this.model, this.property. If the validation fails you must push the failing message onto the validator's this.errors array. A simple example of a validator could be:

import Base from 'ember-validations/validators/base';
import Ember from 'ember';

export default Base.extend({
  call: function() {
    if (Ember.isBlank(this.model.get(this.property))) {
      this.errors.pushObject("cannot be blank");
    }
  }
});

You may want to create a more complex validator that can observer for changes on multiple properties. You should override the init function to accomplish this:

import Base from 'ember-validations/validators/base';
import Ember from 'ember';

export default Base.extend({
  init: function() {
    // this call is necessary, don't forget it!
    this._super.apply(this, arguments);

    this.dependentValidationKeys.pushObject(this.options.alsoWatch);
  },
  call: function() {
    if (Ember.isBlank(this.model.get(this.property))) {
      this.errors.pushObject("cannot be blank");
    }
  }
});

The init function is given access to the this.options which is simply a POJO of the options passed to the validator. dependentValidationKeys is the collection of paths relative to this.model that will be observed for changes. If any changes occur on any given path the validator will automatically trigger.

Inline Validators

If you want to create validators inline you can use the validator function that is part of the ember-validations export:

import EmberValidations, { validator } from 'ember-validations';

User.create({
  validations: {
    'model.name': {
      inline: validator(function() {
        if (this.model.get('canNotDoSomething')) {
          return "you can't do this!"
        }
      })
    }
  }
});

Inside the validator function you have access to this.model which is a reference to the model. You must return an error message that will be attached to the errors array for the property it is created on. Return nothing for the validator to pass.

Alternatively if the property doesn't have any additional validations you can use a more concise syntax:

User.create({
  validations: {
    'model.name': EmberValidations.validator(function() {
      if (this.model.get('canNotDoSomething')) {
        return "you can't do this!"
      }
    })
  }
});

Running Validations

Validations will automatically run when the object is created and when each property changes. isValid states bubble up and help define the direct parent's validation state. isInvalid is also available for convenience.

If you want to force all validations to run simply call .validate() on the object. isValid will be set to true or false. All validations are run as deferred objects, so the validations will not be completed when validate is done. So validate returns a promise, call then with a function containing the code you want to run after the validations have successfully completed.

user.validate().then(function() {
  // all validations pass
  user.get('isValid'); // true
}).catch(function() {
  // any validations fail
  user.get('isValid'); // false
}).finally(function() {
  // all validations complete
  // regardless of isValid state
 user.get('isValid'); // true || false
});

Inspecting Errors

After mixing in EmberValidations into your object it will now have a .errors object. All validation error messages will be placed in there for the corresponding property. Errors messages will always be an array.

import Ember from 'ember';
import EmberValidations from 'ember-validations';

export default Ember.Object.extend(EmberValidations, {
  validations: {
    'model.firstName': { presence: true }
  }
});
import User from 'my-app/models/user';

user = User.create();
user.validate().then(null, function() {
  user.get('isValid'); // false
  user.get('errors.firstName'); // ["can't be blank"]
  user.set('firstName', 'Brian');
  user.validate().then(function() {
    user.get('isValid'); // true
    user.get('errors.firstName'); // []
  })
})

Testing

With Ember QUnit

For Ember Validations to work with Ember QUnit, you must define all your validations in the needs property of the moduleFor call. This will ensure Ember QUnit's isolated container will be able to locate the validations during testing.

import { test, moduleFor } from 'ember-qunit';

moduleFor('controller:user/edit', 'UserEditController', {
  needs: ['service:validations',
          'ember-validations@validator:local/presence',
          'ember-validations@validator:local/length',
          'validator:local/name',
          'validator:local/email'
          ]
});

test('Controller Test', function() { ... });

Where UserEditController uses the built-in presence and length validators, and the locally defined name and email validators.

Test Helpers

To test whether your Ember validations are working correctly, you can use the test helpers:

testValidPropertyValues(propertyName, values [, context ])

testInvalidPropertyValues(propertyName, values [, context ])

  • propertyName (String): the property that you are validating.
  • values (Array): an array of property values to check.
  • context (function) optional: if specified, this function will be called with the object under test as an argument. See example below.
import { test, moduleFor } from 'ember-qunit';
import {
  testValidPropertyValues,
  testInvalidPropertyValues
} from '../../helpers/validate-properties';

moduleFor('controller:user', 'UserController', {
  needs: ['ember-validations@validator:local/presence',
          'ember-validations@validator:local/length'
         ]
});

testValidPropertyValues('firstName', ['Winston', '12345']);
testInvalidPropertyValues('firstName', ['abc', '', null, undefined]);

If a property's validation relies on another property, you can pass a context to the test helper:

testValidPropertyValues('lastName', ['Dog', '12345'], function(subject) {
  subject.set('firstName', 'Boomer');
});

testValidPropertyValues('lastName', ['', null, undefined], function(subject) {
  subject.set('firstName', null);
});

i18n

When you use ember-i18n your Ember.I18n.translations object should contain the following keys under the errors key:

Ember.I18n.translations = {
  errors: {
    inclusion: "is not included in the list",
    exclusion: "is reserved",
    invalid: "is invalid",
    confirmation: "doesn't match {{attribute}}",
    accepted: "must be accepted",
    empty: "can't be empty",
    blank: "can't be blank",
    present: "must be blank",
    tooLong: "is too long (maximum is {{count}} characters)",
    tooShort: "is too short (minimum is {{count}} characters)",
    wrongLength: "is the wrong length (should be {{count}} characters)",
    notANumber: "is not a number",
    notAnInteger: "must be an integer",
    greaterThan: "must be greater than {{count}}",
    greaterThanOrEqualTo: "must be greater than or equal to {{count}}",
    equalTo: "must be equal to {{count}}",
    lessThan: "must be less than {{count}}",
    lessThanOrEqualTo: "must be less than or equal to {{count}}",
    otherThan: "must be other than {{count}}",
    odd: "must be odd",
    even: "must be even"
  }
}

Other Resources

Authors

We are very thankful for the many contributors

Versioning

This library follows Semantic Versioning

Want to help?

Please do! We are always looking to improve this library. Please see our Contribution Guidelines on how to properly submit issues and pull requests.

Legal

DockYard, LLC © 2016

@dockyard

Licensed under the MIT license

More Repositories

1

client_side_validations

Client Side Validations made easy for Ruby on Rails
Ruby
2,686
star
2

postgres_ext

Adds support for missing PostgreSQL data types to ActiveRecord
Ruby
643
star
3

ember-easy-form

Easily build semantic forms in Ember
JavaScript
565
star
4

party_foul

Use GitHub to track your application errors!
Ruby
517
star
5

ruby-destroyed_at

ActiveRecord Mixin for Safe Destroys
Ruby
349
star
6

capybara-email

Test your ActionMailer and Mailer messages with Capybara
Ruby
339
star
7

postgres_ext-serializers

Ruby
324
star
8

client_side_validations-simple_form

Simple Form plugin for ClientSideValidations
JavaScript
254
star
9

ember-appkit-rails

Ember Appkit for Rails
Ruby
238
star
10

ember-suave

Make your Ember App Stylish
JavaScript
179
star
11

ember-one-way-controls

Native one way input
JavaScript
176
star
12

ember-data-route

Common teardown scenario for ember routes backed by a data model
JavaScript
120
star
13

ember-cli-i18n

Simple Internationalization support for ember-cli apps
JavaScript
112
star
14

dismissible_helpers

Ruby
96
star
15

es6_module_transpiler-rails

Transpile ES6 Modules in the Rails Asset Pipeline
JavaScript
87
star
16

capybara-extensions

Complements Capybara with additional finders and matchers.
Ruby
66
star
17

ruby-context_validations

Context Aware Validations for Rails
Ruby
65
star
18

pg_array_parser

Ruby
61
star
19

pages

Easy pages in Rails
Ruby
49
star
20

ember-skeleton

Show fast-loading temporary images in place of an eventual slow-loading image
JavaScript
41
star
21

ember-cli-proxy-fixtures

Ember CLI Proxy Fixtures
JavaScript
38
star
22

client_side_validations-mongoid

Mongoid plugin for ClientSideValidations
Ruby
28
star
23

ruby-easy_auth

Dead simple drop-in identity based Rails authentication
Ruby
28
star
24

ember-admin-bootstrap

Ember Admin with a Twitter Bootstrap Theme
CSS
25
star
25

client_side_validations-formtastic

Formtastic plugin for ClientSideValidations
Ruby
18
star
26

ember-wuphf

JavaScript
17
star
27

postgres_ext-postgis

Ruby
14
star
28

ember-cli-one-script

This addon combines your `vendor.js` and `<your-app-name>.js` into a single file called `app.js`
JavaScript
14
star
29

client_side_validations-turbolinks

Turbolinks Plugin for ClientSideValidations
Ruby
9
star
30

fixtory

Not quite fixtures, not quite factories
Ruby
9
star
31

ember-new-modules-shim

JavaScript
6
star
32

client_side_validations-backbone

Backbone plugin for ClientSideValidations
JavaScript
6
star
33

ember-cli-test-interactions

Ember acceptance test helpers.
JavaScript
3
star
34

minitest-moar

Moar Minitest Pluzsh!
Ruby
3
star
35

ruby-easy_auth-password

Password plugin for EasyAuth
Ruby
3
star
36

mail_congress

Ruby
2
star
37

ruby-easy_auth-oauth2

Ruby
1
star
38

ruby-easy_auth-twitter

Ruby
1
star
39

ruby-easy_auth-linked_in

Ruby
1
star
40

comet

Elixir
1
star