anandts88 / ember-validator

Perform Ember Object Validation
MIT License
5 stars 6 forks source link

Ember Validator

Build Status npm version Dependency Status devDependency Status

  1. Performs validation on ember object.
  2. Provides facility to perform validation on submit or focus out.
  3. Provides facility to perform validation when user starts typing, based on computed property rather using observer.
  4. Returns validation result as promise.
  5. Provides facility to return result as regular object rather than promise.
  6. Supports nested property validation.

Installation

Please add ember-validator to your package.json:

"devDependencies": {
  ...
  "ember-validator": "1.4.0"
}

By default date validator is enabled in the validator, so you may need moment library. If you want to skip importing moment library then set useDateValidator to false in ember-cli-build.js.

var app = new EmberApp(defaults, {
  emberValidator: {
    useDateValidator: false
  }
});

Installation

Running

Running Tests

Building

For more information on using ember-cli, visit http://www.ember-cli.com/.

Issues or Help?

If you find a bug or need help please open an issue on our Github.

How to use?

Import EmberValidator mixin and use this in any Ember Object (route, controller, service).

import EmberValidator from 'ember-validator';

export default Ember.Controller.extend(EmberValidations, {});

If you want to add this mixin in a regular Ember.Object which dont have container support, then use initilizer to reopen EmberValidator and assing container instance to it.

/**
  app/instance-initializers/ember-validator.js
*/
import Ember from 'ember';
import EmberValidator from 'ember-validator';

export default {
  name: 'ember-validator-reopen',
  initialize: function(container) {
    EmberValidator.reopen({
      container: container
    });
  }
}

Ember.Object.extend(EmberValidator, {});

For older version of Ember. But we suggest not to use this approach.

/**
  app/initializers/ember-validator.js
*/
import Ember from 'ember';
import EmberValidator from 'ember-validator';

export default {
  name: 'ember-validator-reopen',
  initialize: function(container) {
    EmberValidator.reopen({
      container: container
    });
  }
}

Ember.Object.extend(EmberValidator, {});

EmberValidator exposes two functions to perform validation.

  1. validateMap
  2. createObjectWithValidator
  3. computedValidateMap
  4. validate

validateMap

This functions is used to validate any object.

import Ember from 'ember';
import EmberValidator from 'ember-validator';

export default Ember.Controller.extend(EmberValidations, {

  actions: {
    var obj = Ember.Object.create({
      userName: null,
      password: null
    });

    var rules = {
      userName: {
        required: {
          message: "Please enter user name"
        }
      },

      password: {
        required: {
          message: "Please enter password"
        }
      }
    };

    var promise = this.validateMap({
      model: obj, // Pass the object you want to validate
      validations: rules // Define validation rules in the form of JSON,
    });

    promise.then(function() {
      // When no errors or object is valid.
    }).catch(function(result) {
      // When there are errors or object is in valid.
      // result.get('errors') -> Returns all error messages
      // result.get('errors') -> Returns first message
      // result.get('isValid') -> Returns true if the object is valid
      // result.get('isInvalid') -> Returns true if the object is invalid
      // result.get('hasError') -> Returns true if the object has errors

      // result.get('userName.errors') -> Returns all error messages related with userName property
      // result.get('userName.errors') -> Returns first message related with userName property
      // result.get('userName.isValid') -> Returns true if userName property is valid
      // result.get('userName.isInvalid') -> Returns true if userName property is invalid
      // result.get('userName.hasError') -> Returns true if userName property has errors
    }).finally(function() {
      // Regardless object is valid or invalid.
    });
  }

});

createObjectWithValidator

Create or reopen object with validations. So validation will fired whenever the property value changes. In short triggers validator by computing property change.

Takes 3 arguments.

For example, if you are defining validation rules for a property called userName for the object called model, then validation result of that property is available in property userNameValidatorResult

model.get('userNameValidatorResult.errors') -> Returns all error messages related with userName property model.get('userNameValidatorResult.errors') -> Returns first message related with userName property model.get('userNameValidatorResult.isValid') -> Returns true if userName property is valid model.get('userNameValidatorResult.isInvalid') -> Returns true if userName property is invalid model.get('userNameValidatorResult.hasError') -> Returns true if userName property has errors

model.get('validatorResultHasError') -> Computed property which returns true if the validation result has any error model.get('validatorResultIsInValid') -> Computed property which returns true if model is not valid model.get('validatorResultIsValid') -> Computed property which returns true if model is valid model.get('validatorResultErrors') -> Computed property which array of all validation errors model.get('validatorResultObjectDirty') -> Computed property which returns true if the object is dirty model.get('validatorResultObjectClean') -> Computed property which returns true if the object is clean

  // app/models/login.js
  import Ember from 'ember';

  export default Ember.Object.extend({
    userName: null,
    password: null
  });  

  // app/routes/login.js
  import Ember from 'ember';
  import EmberValidator from 'ember-validator';
  import LoginModel from 'app/models/login';

  export default Ember.Route.extend(EmberValidations, {
    model: function() {
      var model = LoginModel.create();
      var validations = {
        userName: {
          required: { message: 'Enter username' },
          length: {
            minimum: 4,
            messages: {
              minimum: 'Username is minimum of 4 characters'
            }
          }
        },
        password: {
          required: { message: 'Enter Password' }
        }
      };

      return this.createObjectWithValidator(model, validations, true);
    }
  });

  // app/templates/login.hbs
  <form {{action "login" on="submit"}}>
    <p style="color:red">{{model.userNameValidatorResult.error}}</p>
    {{input type="text" value=model.userName}}
    <p style="color:red">{{model.passwordValidatorResult.error}}</p>
    {{input type="password" value=model.password}}
    <button type="submit">Login</button>
  </form>

computedValidateMap

Same as createObjectWithValidator, but we suggest to use createObjectWithValidator.

  // app/models/login.js
  import Ember from 'ember';

  export default Ember.Object.extend({
    userName: null,
    password: null
  });  

  // app/routes/login.js
  import Ember from 'ember';
  import EmberValidator from 'ember-validator';
  import LoginModel from 'app/models/login';

  export default Ember.Route.extend(EmberValidations, {
    model: function() {
      var model = LoginModel.create();
      var validations = {
        userName: {
          required: { message: 'Enter username' },
          length: {
            minimum: 4,
            messages: {
              minimum: 'Username is minimum of 4 characters'
            }
          }
        },
        password: {
          required: { message: 'Enter Password' }
        }
      };

      this.computedValidateMap({
        model: model,
        validations: validations,
        validateOnDirty: true
      });
    }
  });

  // app/templates/login.hbs
  <form {{action "login" on="submit"}}>
    <p style="color:red">{{model.userNameValidatorResult.error}}</p>
    {{input type="text" value=model.userName}}
    <p style="color:red">{{model.passwordValidatorResult.error}}</p>
    {{input type="password" value=model.password}}
    <button type="submit">Login</button>
  </form>

validate

Use this function if you want to directly validate Ember Object

import Ember from 'ember';
import EmberValidator from 'ember-validator';

export default Ember.Controller.extend({

  actions: {
    var obj = Ember.Object.create(EmberValidations, { // Don't forget to reopen mixin to add container.
      userName: null,
      password: null
    });

    var rules = {
      userName: {
        required: {
          message: "Please enter user name"
        }
      },

      password: {
        required: {
          message: "Please enter password"
        }
      }
    };

    var promise = obj.validate({
      validations: rules // Define validation rules in the form of JSON,
    });

    promise.then(function() {
      // When no errors or object is valid.
    }).catch(function(result) {
      // When there are errors or object is in valid.
    }).finally(function() {
      // Regardless object is valid or invalid.
    });
  }
});

Both validateMap and validate is returning back promise. If you dont want a promise, but only wants the result, then please pass flag noPromise as true

  var result = obj.validate({
    validations: rules,
    noPromise: true
  });

  var result = this.validateMap({
    model: obj,
    validations: rules,
    noPromise: true
  });

  if (result.get('isValid')) {
    // When object is valid
  } else {
    // When object is invalid
  }

  // result.get('errors') -> Returns all error messages
  // result.get('errors') -> Returns first message
  // result.get('isValid') -> Returns true if the object is valid
  // result.get('isInvalid') -> Returns true if the object is invalid
  // result.get('hasError') -> Returns true if the object has errors

  // result.get('userName.errors') -> Returns all error messages related with userName property
  // result.get('userName.errors') -> Returns first message related with userName property
  // result.get('userName.isValid') -> Returns true if userName property is valid
  // result.get('userName.isInvalid') -> Returns true if userName property is invalid
  // result.get('userName.hasError') -> Returns true if userName property has errors

Nested Property Validations

Supports to validation of object with an object.

In case of validation of a nested property, if you want to set validation result of the nested property in different parameter then supply 'errorProperty' along with validation rules.

 var validations = {
   'orders.detail': {
     errorProperty: 'orderDetails',
     required: true
   }
 };

 var result = this.validateMap({
   model: obj,
   validations: validations,
   noPromise: true
 });

 result.get('orderDetails.hasError');

Validators

required

Validate the property has value.

Options

  required: { message: 'Please enter a value' }
  required: 'Please enter a value'
  required: true

notrequired

Validate the property dont have value.

Options

  notrequired: { message: 'Value must be empty' }
  notrequired: 'Value must be empty'
  notrequired: true

boolean

Validate the property is boolean and has value true or false.

Options

messages

If you want to override message of individual rule.

  boolean: {
    required: true
    message: 'must be true'
  }

  boolean: {
    notrequired: true
    messages: {
      boolean: 'must be boolean'
      notrequired: 'must be false'
    }
  }

  boolean: 'must be true'
  boolean: true

equals

Validate whether the property equals to the specified value.

Options

  equals: {
    message: 'Only myusername is accepted',
    accept: 'myusername'
  }

contains

Validate property with array of values, and the array of values contains/notcontains the property.

Options

Messages

If you want to override message of individual rule.

contains: {
  exclude: ['A', 'B', 'C'],
  messages: {
    exclude: 'Please enter valid value'
  }
}

contains: {
  exclude: ['A', 'B', 'C'],
  message: 'Please enter valid value'
}

contains: {
  excludeRange: [1, 10],
  messages: {
    excludeRange: 'cannot be between 1 and 10'
  }
}

contains: {
  include: ['A', 'B', 'C'],
  messages: {
    include: 'Please enter valid value'
  }
}

contains: {
  include: ['A', 'B', 'C'],
  message: 'Please enter valid value'
}

contains: {
  includeRange: [1, 10],
  messages: {
    includeRange: 'cannot be between 1 and 10'
  }
}

pattern

Validate poperty with passed regular expression

Options

messages

If you want to override message of individual rule.

  pattern: {
    with: /^([a-zA-Z])+$/,
    messages: {
      with: 'Must be alphabets'  
    }
  }

  pattern: {
    with: /^([a-zA-Z])+$/,
    message: 'Must be alphabets'  
  }

  pattern: {
    without: /^([a-zA-Z])+$/,
    messages: {
      without: 'Must not be alphabets'  
    }
  }

  pattern: {
    without: /^([a-zA-Z])+$/,
    message: 'Must not be alphabets'  
  }

  pattern: {
    array: [
      {
        with: /^([a-zA-Z])+$/,
        message: 'Must be alphabets'  
      },
      {
        without: /^([a-zA-Z])+$/, // Different expression
        message: 'Must be alphabets' // Message to be displayed
      }
    ]
  }

email

Validate whether the property is email.

Options

  email: {
    message: "Invalid email"
  }

  email: true
  email: 'Invalid email'

  email: {
    with: /^(([^<>()\[\]\\.,;:\s@\"]+(\.[^<>()\[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
    message: "Invalid email"
  }

ssn

Validate whether the property is ssn. Default pattern of ssn NNN-NN-NNNN. change this by setting with property

Options

  ssn: {
    format1: true,
    message: "Invalid ssn"
  }

  ssn: {
    all: true,
    message: "Invalid ssn"
  }

  ssn: true
  ssn: 'Invalid ssn'

zip

Validate whether the property is valid zip. Default pattern of ssn NNNNN or NNNNN-NNNN. change this by setting with property

Options

  zip: {
    message: "Invalid zip"
  }

  zip: true
  zip: 'Invalid zip'

phone

Validate whether the property is valid phone. Default supported phone number pattern are.

Options

  phone: {
    format1: true,
    format5: true,
    message: "Invalid phone"
  }
  // This matches format1 and format4 patterns of phone number

  phone: {
    all: true
    message: "Invalid phone"
  }

  phone: true
  phone: 'Invalid phone'

length

Validate length of property

Options

Messages

If you want to override message of individual rule.

  length: {
    minimum: 4,
    maximum: 20,
    messages: {
      minimum: 'Must be more than 4 characters',
      maximum: 'Must be less than 20 characters'
    }
  }

  length: {
    is: 4,
    messages: {
      is: 'Must be contains 4 characters'
    }
  }

  length: {
    is: 4,
    message: 'Must be contains 4 characters'
  }

  length: {
    is: 4,
    message: 'Must be contains 4 characters'
    tokenizer: function(value) {
      return value.split(' ');
    }
  }

numeric

Validates property is a number, comma separated numbers also supported. Default pattern of numbers /^\d+(,\d{3})(.\d)?$/ You can change this by setting pattern property of the options. By default maximum allowed decimal length is 12 and maximum allowed fraction length is 2.

Options

Messages

If you want to override message of individual rule.

  numeric: {
    integer: true,
    messages: {
      integer: 'must be an integer'
    }
  }

  numeric: {
    lessThan: 10,
    messages: {
      lessThan: 'must be less than 10'
    }
  }

  numeric: {
    greaterThan: "10,000.12",
    decimal: 5,
    fraction: 2,
    messages: {
      greaterThan: 'must be greater than 10,000.12'
      decimal: 'must be max of 5 decimal digits',
      fraction: 'must be max of 2 fraction digits'
    }
  }

  numeric: {
    greaterThan: "10,000.12",
    decimal: 5,
    fraction: 2,
    message: 'Valid number'
    messages: {
      greaterThan: 'must be greater than 10,000.12'
    }
  }

date

Perform date validation. Property can be Date object, moment object or string. If the property is string then set format option

Options

The below two are options for same, before, after, beforeSame and afterSame

Messages

If you want to override message of individual rule.

  date: {
    weekend: true,
    messages: {
      weekend: 'must not be weekend'
    }
  }

// If property date is 'Nov/01/2015'
  date: {
    format: 'MMM/DD/YYYY',
    before: {
      target: 'Oct/31/2015',
      format: 'MMM/DD/YYYY'
    },
    afterSame: {
      target: new Date()
    },
    messages: {
      before: 'date must be before Oct/31/2015',
      afterSame: 'date must be after new Date()'
    }
  }

Conditional Validators

In some cases we may want to execute the function only if certain conditions are satisfied. Please use if and unless options in validation rules.

  userName: {
    length: {
      'if': function(model, property) {
        return true;
      }
    }
  }

  password: {
    numeric: {
      unless: function(model, property) {
        return false;
      }
    }
  }

Conditional validators are also added in property level, so it wont validate any validators defined for the property.

  userName: {
    'if': function(model, property) {
      return true;
    },
    length: {

    }
  }

  password: {
    unless: function(model, property) {
      return false;
    },
    numeric: {

    }
  }

Custom Validators

You can place your custom validators into my-app/app/validators/<name>:

  /**
    my-app/app/validators/myvalidator.js
  */
  import Validator from 'ember-validator/validators/validator';

  export default Validator.extend({
     perform: function() {
       // Do your validation login here.
     }
  });

  userName: {
    myvalidator: {
      ...
    }
  }

Custom Inline Validators

If you want to create validators on the fly then,

import { inlineValidator } from 'ember-validator';

{
  userName: {
    custom: inlineValidator(function(model, property) {
      return 'Error message';
    })
  }
}

References

  1. Dockyard ember-validations
  2. Daniel Kuczewski ember-validations

NPM

NPM