Giter Club home page Giter Club logo

ember-changeset-validations's Introduction



ember-changeset-validations: Validations for ember-changeset


Download count all time GitHub Actions Build Status npm version Ember Observer Score

ember-changeset-validations is a companion validation library to ember-changeset. It's really simple to use and understand, and there are no CPs or observers anywhere – it's mostly just functions.

Since ember-changeset is required to use this addon, please see documentation there on how to install and use changesets.

To install if your app is on ember-source >= 3.13:

ember install ember-changeset-validations

To install if your app is on ember-source < 3.13:

ember install [email protected]

Starting with v4 this addon does not install ember-changeset so make sure to list it in your devDependencies (for apps) or dependencies (for addons).

Watch a 6-part video series on ember-changeset and ember-changeset-validations presented by EmberScreencasts.

Usage

This addon updates the changeset helper by taking in a validation map as a 2nd argument (instead of a validator function). This means that you can very easily compose validations and decouple the validation from the underlying model.

{{! application/template.hbs}}
<DummyForm
    @changeset={{changeset user EmployeeValidations}}
    @submit={{action "submit"}}
    @rollback={{action "rollback"}} />

<DummyForm
    @changeset={{changeset user AdminValidations}}
    @submit={{action "submit"}}
    @rollback={{action "rollback"}} />

A validation map is just a POJO (Plain Old JavaScript Object). Use the bundled validators from ember-changeset-validations to compose validations or write your own. For example:

// validations/employee.js
import {
  validatePresence,
  validateLength,
  validateConfirmation,
  validateFormat
} from 'ember-changeset-validations/validators';
import validateCustom from '../validators/custom'; // local validator
import validatePasswordStrength from '../validators/password-strength'; // local validator

export default {
  firstName: [
    validatePresence(true),
    validateLength({ min: 4 })
  ],
  lastName: validatePresence(true),
  age: validateCustom({ foo: 'bar' }),
  email: validateFormat({ type: 'email' }),
  password: [
    validateLength({ min: 8 }),
    validatePasswordStrength({ minScore: 80 })
  ],
  passwordConfirmation: validateConfirmation({ on: 'password' })
};

Then, you can use the POJO as a property on your Component or Controller and use it in the template:

import Component from '@glimmer/component';
import EmployeeValidations from '../validations/employee';
import AdminValidations from '../validations/admin';

export default class EmployeeComponent extends Component {
  EmployeeValidations = EmployeeValidations;
  AdminValidations = AdminValidations;
}
<DummyForm
    @changeset={{changeset user this.EmployeeValidations}}
    @submit={{action "submit"}}
    @rollback={{action "rollback"}} />

Moreover, as of 3.8.0, a validator can be an Object or Class with a validate function.

import fetch from 'fetch';

export default class PersonalNoValidator {

  async validate(key, newValue, oldValue, changes, content) {
    try {
      await fetch(
        '/api/personal-no/validation',
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ data: newValue })
        }
      );

      return true;
    } catch (_) {
      return 'Personal No is invalid';
    }
  }
}

When creating the Changeset programmatically instead of using the changeset helper, you will have to apply the lookupValidator function to convert the POJO to a validator function as expected by Changeset:

import Component from '@glimmer/component';
import EmployeeValidations from '../validations/employee';
import lookupValidator from 'ember-changeset-validations';
import Changeset from 'ember-changeset';

export default class ChangesetComponent extends Component {
  constructor() {
    super(...arguments);
    this.changeset = new Changeset(this.model, lookupValidator(EmployeeValidations), EmployeeValidations);
  }
}
<DummyForm
    @changeset={{this.changeset}}
    @submit={{action "submit"}}
    @rollback={{action "rollback"}} />

ember-changeset and ember-changeset-validations both also support creating changesets from promises. However, because that will also return a promise, to render in your template you will need to use a helper like await from ember-promise-helpers.

Validator API

ember-changeset-validations utilizes ember-validators as a core set of validators.

All validators take a custom message option.

presence

Validates presence/absence of a value.

πŸ‘‰ All Options

{
  propertyName: validatePresence(true), // must be present
  propertyName: validatePresence(false) // must be blank
  propertyName: validatePresence({ presence: true }) // alternative option syntax
  propertyName: validatePresence({ presence: true, ignoreBlank: true }) // If ignoreBlank true, treats an empty or whitespace string as not present.
}

on option for presence

Only validates for presence if any of the other values are present

{
  password: validatePresence({ presence: true, on: 'ssn' })
  password: validatePresence({ presence: true, on: [ 'ssn', 'email', 'address' ] })
  password: validatePresence({ presence: false, on: 'alternative-login' })
}

⬆️ back to top

length

Validates the length of a String or an Array.

πŸ‘‰ All Options

{
  propertyName: validateLength({ min: 1 }), // 1 or more
  propertyName: validateLength({ max: 8 }), // up to 8
  propertyName: validateLength({ min: 1, max: 8 }), // between 1 and 8 (inclusive)
  propertyName: validateLength({ is: 16 }), // exactly 16
  propertyName: validateLength({ allowBlank: true }) // can be blank
}

⬆️ back to top

date

This API accepts valid Date objects or a Date in milliseconds since Jan 1 1970, or a functiom that returns a Date. Strings are currently not supported. It is recommended you use use native JavaScript or you library of choice to generate a date from your data.

{
  propertyName: validateDate({ before: new Date('3000-01-01') }), // must be before 1st Jan. 3000
  propertyName: validateDate({ onOrBefore: Date.parse(new Date('3000-01-01')) }), // must be not after 1st Jan. 3000
  propertyName: validateDate({ after: new Date('3000-01-01') }), // must be after 1st Jan. 3000
  propertyName: validateDate({ onOrAfter: new Date('3000-01-01') }), // must be not before 1st Jan. 3000
  propertyName: validateDate({ onOrAfter: () => new Date() }), // must not be in the past
  propertyName: validateDate({ onOrAfter: '3000-01-01' }), // Error
}

⬆️ back to top

number

Validates various properties of a number.

πŸ‘‰ All Options

{
  propertyName: validateNumber({ is: 16 }), // exactly 16
  propertyName: validateNumber({ allowBlank: true }), // can be blank
  propertyName: validateNumber({ integer: true }), // must be an integer
  propertyName: validateNumber({ lt: 10 }), // less than 10
  propertyName: validateNumber({ lte: 10 }), // less than or equal to 10
  propertyName: validateNumber({ gt: 5 }), // greater than 5
  propertyName: validateNumber({ gte: 10 }), // greater than or equal to 10
  propertyName: validateNumber({ positive: true }), // must be a positive number
  propertyName: validateNumber({ odd: true }), // must be an odd number
  propertyName: validateNumber({ even: true }), // must be an even number
  propertyName: validateNumber({ multipleOf: 7 }) // must be a multiple of 7
}

⬆️ back to top

inclusion

Validates that a value is a member of some list or range.

πŸ‘‰ All Options

{
  propertyName: validateInclusion({ list: ['Foo', 'Bar'] }), // must be "Foo" or "Bar"
  propertyName: validateInclusion({ range: [18, 60] }), // must be between 18 and 60
  propertyName: validateInclusion({ allowBlank: true }), // can be blank
}

⬆️ back to top

exclusion

Validates that a value is a not member of some list or range.

πŸ‘‰ All Options

{
  propertyName: validateExclusion({ list: ['Foo', 'Bar'] }), // cannot be "Foo" or "Bar"
  propertyName: validateExclusion({ range: [18, 60] }), // must not be between 18 and 60
  propertyName: validateExclusion({ allowBlank: true }), // can be blank
}

⬆️ back to top

format

Validates a String based on a regular expression.

πŸ‘‰ All Options

{
  propertyName: validateFormat({ allowBlank: true }), // can be blank
  propertyName: validateFormat({ type: 'email' }), // built-in email format
  propertyName: validateFormat({ type: 'phone' }), // built-in phone format
  propertyName: validateFormat({ type: 'url' }), // built-in URL format
  propertyName: validateFormat({ regex: /\w{6,30}/ }) // custom regular expression
  propertyName: validateFormat({ type: 'email', inverse: true }) // passes if the value doesn't match the given format
}

⬆️ back to top

confirmation

Validates that a field has the same value as another.

πŸ‘‰ All Options

{
  propertyName: validateConfirmation({ on: 'password' }), // must match 'password'
  propertyName: validateConfirmation({ allowBlank: true }), // can be blank
}

⬆️ back to top

Writing your own validators

Adding your own validator is super simple – there are no Base classes to extend! Validators are just functions. All you need to do is to create a function with the correct signature.

Create a new validator using the blueprint:

ember generate validator <name>

ember-changeset-validations expects a higher order function that returns the validator function. The validator (or inner function) accepts a key, newValue, oldValue, changes, and content. The outer function accepts options for the validator.

Synchronous validators

For example:

// validators/custom.js
export default function validateCustom({ min, max } = {}) {
  return (key, newValue, oldValue, changes, content) => {
    // validation logic
    // return `true` if valid || error message string if invalid
  }
}

Asynchronous validators

In addition to conforming to the function signature above, your validator function should return a Promise that resolves with true (if valid), or an error message string if invalid.

For example:

export default function validateUniqueness(opts) {
  return (key, newValue, oldValue, changes, content) => {
    return new Promise((resolve) => {
      // validation logic
      // resolve with `true` if valid || error message string if invalid
      resolve(true);
    });
  };
}

Using custom validators

That's it! Then, you can use your custom validator like so:

// validations/custom.js
import { validateLength } from 'ember-changeset-validations/validators';
import validateUniqueness from '../validators/unique';
import validateCustom from '../validators/custom';

export default {
  firstName: validateCustom({ min: 4, max: 8 }),
  lastName: validateCustom({ min: 1 }),
  email: [
    validateFormat({ type: 'email'}),
    validateUniqueness()
  ]
};

Testing

Since validators are higher order functions that return functions, testing is straightforward and requires no additional setup:

import validateUniqueness from 'path/to/validators/uniqueness';
import { module, test } from 'qunit';

module('Unit | Validator | uniqueness');

test('it does something', function(assert) {
  let key = 'email';
  let options = { /* ... */ };
  let validator = validateUniqueness(options);

  assert.equal(validator(key, undefined), /* ... */);
  assert.equal(validator(key, null), /* ... */);
  assert.equal(validator(key, ''), /* ... */);
  assert.equal(validator(key, '[email protected]'), /* ... */);
});

Validation composition

Because validation maps are POJOs, composing them couldn't be simpler:

// validations/user.js
import {
  validatePresence,
  validateLength
} from 'ember-changeset-validations/validators';

export default {
  firstName: validatePresence(true),
  lastName: validatePresence(true)
};

You can easily import other validations and combine them using Object.assign.

// validations/adult.js
import UserValidations from './user';
import { validateNumber } from 'ember-changeset-validations/validators';

export const AdultValidations = {
  age: validateNumber({ gt: 18 })
};

export default Object.assign({}, UserValidations, AdultValidations);

Custom validation messages

Each validator that is a part of this library can utilize a message property on the options object passed to the validator. That message property can either be a string or a function.

If message is a string, you can put particular placeholders into it that will be automatically replaced. For example:

{
  propertyName: validatePresence({ presence: true, message: '{description} should be present' })
}

{description} is a hardcoded placeholder that will be replaced with a normalized version of the property name being validated. Any other placeholder will map to properties of the options object you pass to the validator.

Message can also accept a function with the signature (key, type, value, context). Key is the property name being validated. Type is the type of validation being performed (in the case of validators such as number or length, there can be a couple of different ones.) Value is the actual value being validated. Context maps to the options object you passed to the validator.

If message is a function, it must return the error message as a string.

Overriding validation messages

If you need to be able to override the entire validation message object, simply create a module at app/validations/messages.js, exporting a POJO with the following keys:

// app/validations/messages.js
export default {
  inclusion: // '{description} is not included in the list',
  exclusion: // '{description} is reserved',
  invalid: // '{description} is invalid',
  confirmation: // "{description} doesn't match {on}",
  accepted: // '{description} must be accepted',
  empty: // "{description} can't be empty",
  blank: // '{description} must be blank',
  present: // "{description} can't be blank",
  collection: // '{description} must be a collection',
  singular: // "{description} can't be a collection",
  tooLong: // '{description} is too long (maximum is {max} characters)',
  tooShort: // '{description} is too short (minimum is {min} characters)',
  between: // '{description} must be between {min} and {max} characters',
  before: // '{description} must be before {before}',
  onOrBefore: // '{description} must be on or before {onOrBefore}',
  after: // '{description} must be after {after}',
  onOrAfter: // '{description} must be on or after {onOrAfter}',
  wrongDateFormat: // '{description} must be in the format of {format}',
  wrongLength: // '{description} is the wrong length (should be {is} characters)',
  notANumber: // '{description} must be a number',
  notAnInteger: // '{description} must be an integer',
  greaterThan: // '{description} must be greater than {gt}',
  greaterThanOrEqualTo: // '{description} must be greater than or equal to {gte}',
  equalTo: // '{description} must be equal to {is}',
  lessThan: // '{description} must be less than {lt}',
  lessThanOrEqualTo: // '{description} must be less than or equal to {lte}',
  otherThan: // '{description} must be other than {value}',
  odd: // '{description} must be odd',
  even: // '{description} must be even',
  positive: // '{description} must be positive',
  multipleOf: // '{description} must be a multiple of {multipleOf}',
  date: // '{description} must be a valid date',
  email: // '{description} must be a valid email address',
  phone: // '{description} must be a valid phone number',
  url: // '{description} must be a valid url'
}

In the message body, any text wrapped in single braces will be replaced with their appropriate values that were passed in as options to the validator. For example:

import buildMessage from 'ember-changeset-validations/utils/validation-errors';
// validators/custom.js
export default function validateIsOne(options) {
  return (key, newValue, oldValue, changes, content) => {
    return newValue === 1 || buildMessage(key, { type: 'isOne', value: newValue, context: options });
  }
}
// validations/foo.js
export default {
  mySpecialNumber: validateIsOne({ foo: 'foo' }})
};

The above will look for a key isOne in your custom validation map, and use keys defined on the options object (in this case, foo) to replace tokens. With the custom validator above, we can add:

// app/validations/messages.js
export default {
  isOne: '{description} must equal one, and also {foo}'
}

Will render: My special number must equal one, and also foo.

Raw error output

By default, ember-changeset-validations returns the errors as plain strings. In some situations, it may be preferable for the developer that the library returns a description of the errors; internationalisation (i18n) for example, or finer-grained error output.

To have ember-changeset-validations return such data structure, add the following to you config/environment.js

let ENV = {
  ...
  'changeset-validations': { rawOutput: true }
  ...
}

This will return an object with the following structure, that you can then pass to your applications's error processing:

{
  value, // the value to validate
  type, // the type of the error (`present`, `blank`...)
  message, // the **unprocessed** error message
  context: {
    description // the description of the field
    // ...and other options given to configure the validator
  }
}

Contributors

We're grateful to these wonderful contributors who've contributed to ember-changeset-validations:

Installation

  • git clone <repository-url> this repository
  • cd ember-changeset-validations
  • npm install

Running

Running Tests

  • npm test (Runs ember try:each to test your addon against multiple Ember versions)
  • ember test
  • ember test --server

Building

  • ember build

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

ember-changeset-validations's People

Contributors

acburdine avatar ahmadsoe avatar alexabreu avatar alexlafroscia avatar bcardarella avatar betocantu93 avatar brandynbennett avatar cibernox avatar daniel-xu avatar dhaulagiri avatar dustinfarris avatar greenkeeperio-bot avatar jbescoyez avatar jeffreybiles avatar jhitchins88 avatar josemarluedke avatar manquer avatar mcfiredrill avatar nickschot avatar nucleartide avatar pangratz avatar poteto avatar redsandro avatar rkrishnan8594 avatar sergeastapov avatar snewcomer avatar urbany avatar velrest avatar xiphiasuvella avatar xomaczar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ember-changeset-validations's Issues

How to use two validation files for same page?

Hi,

How to couple two validation files as one?
For example, I have two different cards in my page, for which I have two separate validation files.
I have them separately because, I can either create and submit (for which both the validation files need to be considered), else if I edit the already present details, I can edit a separate card & save.

Is there any way to achieve the above feature? Kindly guide.

Thanks in advance!

buildMessage function not getting message by type

Running ember-changeset-validations 1.2.4 and ember 2.10 / ember data 2.10.

My validation file looks like this and I was getting back the default case "is invalid" error message instead of the expected standard message for 'present':

import { validatePresence } from 'ember-changeset-validations/validators';

export default {
  amount: validatePresence({ presence: true })
}; 

I went into the buildMessage function and logged values until I noticed that the return in this line for the get of the type from the messages object was undefined:

return formatMessage(get(messages, type), assign({ description }, context));

I also noticed on further logging values that the "messages" object held all of its values in an object in a "defaults" property. As soon as I added defaults the message was displaying properly:

return formatMessage(get(messages.defaults, type), assign({ description }, context));

Even then, while the standard message content is there, the "description" doesn't seem to be added to the beginning of the message, but it seems like it should be based on what I'm seeing in the messages.js (i.e. the description variable is made a part of the message there). That said, this appends the description to the front of what's returned in the buildMessage:

return formatMessage(description + ' ' + get(messages.defaults, type), assign({ description }, context));

clash with ember-validations messages

Hi there,

It seems like there is a bit of a clash happening with the default messages. The addon supports using a custom message map, but the way that the file is found iterates through all the modules (incl. addons) this results in the ember-validations/messages.js file being picked up and applied.

Looks like its happening here in the get-messages.js util file.

I'm wondering if you know of any work around? I'm happy to submit a PR that might fix this as well, but any hints would be welcome πŸ˜ƒ. I would like to remove ember-validations, but we have it used throughout our application and I'd rather slowly convert it our larger forms to ember-changesets over time.

Thanks!

Version

1.2.4

Test Case

https://ember-twiddle.com/cbe30296bfa00125a43447eb68f8b4a1

Steps to reproduce

  • Delete the value "Jim" from the field "firstName"

Expected Behavior

The default messages that come with ember-changeset-validations should be used.

Actual Behavior

The message, 'is invalid' is used instead because the get-messages function finds the ember-validations/messages.js file and so can't match the type to a message template.

Documentation Needed: Argument definitions/usage for a custom validator

The documentation currently shows that you can create a custom validator and which arguments are provided into the function, however, nowhere is it explained what each of these arguments actually IS. For example, I have a validator that is dependent on two values from the changeset and I thought getting the property from the changes argument would work but in some cases, I am getting undefined. Whether or not this is a bug or just an issue in my code is irrelevant. The problem here is that there is no indication on what these args mean or how they should be used. key, oldValue and newValue are easy enough, but what about changes and content?

Feature Request: validatePresenceOn

Suggested Addition

The validatePresenceOn validator: validate presence if other field(s) are also there.

How it works

export default {
  lastName: validatePresenceOn('firstName'),
  password: validatePresenceOn([ 'email', 'ssn', 'address', ])
}

Alternatively, it could be an option for the validatePresence validator:

export default {
  lastName: validatePresence({ on: 'firstName' }),
  password: validatePresence({ on: [ 'email', 'ssn', 'address', ] })
}

This is something I wrote as a custom validator, so if it's something you would like, I can put up a PR.

Promise-based validation example and best practices?

Hey,

Thanks for the great library. I've noticed that you can return a promise from a custom validator leading me to believe I could write validators that query a remote backend. The most common use-case for this would be checking for other instances of a model with the same value (unique email for example).

It would be great to see a more verbose example this. I'm not sure what is the best way of getting access to the data store as an injected dependency. From my understanding (based largely on this SO answer) you would need to declare the validator as an Object and register it with the container to be able to inject the store dynamically.

I may be overthinking this but would like to hear about some best practices when needing to interact with other models during validation.

How to observe model/changeset properties?

I've added ember-changeset-validations to my Ember app today. I have some computed properties which observe model. They don't work anymore. What am I missing? How can I observe changeset properties?

Multi field validation (i.e. one of must be present) can result in incorrect `isValid` state

This might be more of a feature request than a bug (at least with the provided validators).

I have implemented a validator which checks for presence OR for the presence of other fields (so at least one of the fields must be "present" for the validator to pass). Currently there is no way to call validation for these other fields as the actual changeset is not passed to the validator function (just the changes and the model).

The only "workaround" right now is to call validate on the main changeset validating all fields, but this means that before that happens, the isValid state is incorrect (and errors for said fields might be present on the changeset).

This is likely also an issue for the validator proposed in #189 / #191

How to handle validators that return promises

From @brancusi

Hi,

Wondering how I can handle validators that return promises.

Trying to create a validator that checks if a name is available by calling some api/name_check service.

https://ember-twiddle.com/d4ec3c55c540d6da68da36ba79250590?fileTreeShown=false&openFiles=validations.user.js%2C

If you type anything in firstName you will notice [object Object] instead of a passing validation.

I noticed there was work done on handling validators that return promises, but I can't see how it actually works.

Thank you

Skipping a validator

I have a validation file which holds a complete set of validators for a particular template. The template conditionally renders certain fields. Even when those fields are not rendered the validators appear to be triggering an error on the changeset (since I am using validatePresence and nothing is being entered, since it is not rendered). Is there a way to skip a validator?

Initial changset validation state

Given a changeset reacts to changes, the default state is always valid until a change is made on each property.

Is there a way to force a validation to run on the entire changeset on construction?

e.g.

let model = {};

let validations = Ember.Object.create({
  myKey: validatePresence(true)
});

let changeset = new Changeset(model, lookupValidator(validations), validations);
console.log(changeset.get('isValid')); // true, but the changeset is invalid

changeset.validate();
console.log(changeset.get('isValid')); // false, as expected

"validatePresence on" not updating

validatePresence({ presence: true, on: 'email' })

Version

2.1.0

This is probably me misunderstanding the behavior of the validator.

Steps to reproduce

export default {
	email: [
		validatePresence(true),
		validateFormat({ type: 'email' }),
	],
	password: [
		validatePresence({ presence: true, on: 'email' }),
	],
}

Expected Behavior

Empty password validates fine, but

  1. turns invalid once an email is entered, or
  2. turns invalid after focused after email is entered.

Actual Behavior

Empty password stays valid, focused or not.
Once you type in a password and then remove it again then the validator fails (as expected).

How to validate nested keys?

Given the following Ember.Object:

Ember.Object.create({
  title: 'Hello',
  contactNumber: {
    type: '07594378348',
    number: ''
  }
})

How do I define validations to validate the contactNumber.number field?

{
  title: validatePresence(true),
  contactNumber: ... ?
}

Changing validation error messages at runtime

Hi, great addon.

I'm wondering, if there is a recommended way of changing validation messages at runtime?

I know it's possible to change it initially by defining a module that matches /validations\/messages$/gi but it's only loaded once.

define(['foo/validations/messages'], () => {
  return {default: {
     ...messages
  }};
})

If a user changes its locale, it's impossible to update validation messages from what i can tell.
Are there any known workarounds for supporting this kind of use-case?

Show loading during async validation

During one of our validations, we hit an external service that sometimes takes up to 2 seconds. Is there a property or event that I can hook into so I can show loading while until the async validator completes?

Unable to set a custom validation on two or more fields

Hi!, i can't make a customValidator that check two fields in the changeset. The current changeset is hidden from validator for any reason, or simply i don't find a way go get it?

I think my usercase isn't very strange, i have to date and time fields that needs to be coherent between them.

Feature Request: Public API for getDescriptionFor

In my app, the propertyName for the input does not always map to the displayed label; this would be even more common in localised products.

For example:

<label for={{concat type "-" property}}>{{spanishProperty}}</label>
<input
  class="u-full-width"
  type={{type}}
  id={{concat type "-" property}}
  value={{get changeset property}}
  oninput={{action (mut (get changeset property)) value="target.value"}}
  onblur={{action validateProperty}}>
{{#if (get changeset.error property)}}
  <small>
  	<ul>
    	{{#each (get (get changeset.error property) "validation") as |message|}}
    		<li>{{message}}</li>
    	{{/each}}
    </ul>
  </small>
{{/if}}

While it is possible to change the messages hash per language, the hardcoded {description} just takes the propertyName and normalises it.

Would it be possible to allow the app/utils/messages.js file to be overridden, in order to allow more complex names for the {description} placeholder?

Is there anyway to stop the next validator on the same field if one is invalid?

Hi,

Thanks for creating a great ember addon.

Here is scenario:
I want to validate email field as a email and unique in DB (the uniqueEmail validator will make a request to server to check unique)

 email: [
      validateFormat({ type: "email" }),
      uniqueEmail()
    ]

I think it would be nice if we check unique email just in case passed validateFormat only (for performance). Is there anyway to do it?

Thanks

Trouble using ember-validators with ember-changeset-validators

In a legacy app, I've got some code that uses ember-validators, but I'm starting to add new functionality using ember-changeset-validators.
I discovered that just having ember-changset-validators installed in the same app breaks my old ember-validations code. Now when the old ember-validations code goes to look up the 'presence' validator, it somehow comes back with the validatesPresence function from e-c-v. Then it can't call validator.create, and the app screeches to a halt.

I know long term I want to get off of ember-validations, but for now I need to use both at once, e-v and e-c-v. One way to fix would be to update the names for 'validator:presence' etc to not clash with the names from e-v. Any other suggestions for fixing or working around?

`validateNumber` does not handle `null` value properly

First off, great project! It has radically simplified/standardized our forms and validation handling.

Small issue with validateNumber though. From playing around in my console (after struggling with false-positives in some tests):

> validate
function validateNumber() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    return function (key, value) {
      var numValue = Number(value);
      var…

undefined is fine...

> validate({ })('someKey', undefined)
"Some key must be a number"

> validate({ allowBlank: false })('someKey', undefined)
"Some key must be a number"

... but null...?

> validate({ })('someKey', null)
true
> validate({ allowBlank: false })('someKey', null)
true

https://github.com/DockYard/ember-changeset-validations/blob/master/addon/validators/number.js#L52 seems to be the issue.

> Number(undefined)
NaN
> Number(null)
0

Because JavaScript.

Will attempt a PR this weekend if possible.

Cannot find module 'ember-changeset-validations/validators'

Version

1.2.4

Test Case

// test.js

import { validatePresence } from 'ember-changeset-validations/validators';
console.log(validatePresence);

Steps to reproduce

npm install ember-changeset-validations
node test.js

Expected Behavior

I'd expect to see the validatePresence function declaration outputted in the logs.

Actual Behavior

The module cannot be found:

Cannot find module 'ember-changeset-validations/validators'

Not sure if I'm missing something obvious here, but I tested this within an Ember app and had the same result. Wasn't sure if the problem was with my build process or something, so I thought I'd try the same thing in a single file (as above) and got the same result.

Tested with node 6.9.1

Email Validation Update?

Currently,

email: /^[a-zA-Z0-9.!#$%&'*+/=?^_{|}~-]+@a-zA-Z0-9?(?:.a-zA-Z0-9?)*$/`

This validation allows emails for emails with invalid format, for example: "y@y", this could cause problems for those who use the email validator to check emails but also use a database validator to store authentication credentials.

I'm not sure if the validation was intended this way. Another validator I have found to work is:

email: /^(([^<>()\[\]\\.,;:\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,}))$/

Update an already existing model

Hi @poteto & others,

I am trying to have more than one validated-form in the page, for the same model, taking a changeset.
Different fields will be applicable for each form. But certain fields in one form, is a computed property of another form (the computed property is given in the model itself, so when we save / get, field is computed and received).

Since they are different forms, and each form creates a new model, the model is not updating the other form. Is there any way in which this can be achieved?

Please find this Twiddle for a similar demo.

Many thanks in advance!

ValidatePresence has opposite behavior

sorry I don't have time to set up a dummy link for this but I noticed it twice now...

ember-source: "2.13.3"
ember-changeset: "^1.3.0",
ember-changeset-validations: "^1.2.7",

image

export default {
  url: [
    validatePresence(true),
  ],

regex not working as expected with boolean fields in ValidateFormat

Hello,

I know you won't answer questions, but I tought it would be good to share this. I stumbled upon an issue with validateFormat using regex when trying to check a boolean field. I suspect this simply cannot be used for boolean fields.

More info here:

https://stackoverflow.com/questions/44246791/ember-changeset-validations-not-working-as-expected-with-a-boolean

Maybe this should be stated more clearly in the docs?

Thanks and keep up the good work!

Validator introspection

I want to put * on the required fields in a form if the Presence validator of those field attirbutes is present and set to true. For example, I've have a changeset object and an attribute name say title. How can I introspect the changeset to see if title has a presence validator and that presence validator has been set to true?

`presence` messages are backwards

Version

Starts with this commit in latest master

Test Case

Steps to reproduce

Expected Behavior

When using presence(true) I expect the message to say {key} can't be blank.

When using buildMessage('present') I expect {key} can't be blank.
When using buildMessage('blank') I expect {key} must be blank.

Actual Behavior

When using presence(true) the message says{key} must be blank.
On these lines the type that is returned when the value is blank is blank. In the new mapping blank points to the correct message. However, the logic flips the type to present which is the wrong message.

When using buildMessage('present') I get {key} must be blank.
When using buildMessage('blank') I get {key} can't be blank.

validateFormat regex won't allow blank values

Version

1.2.10

Test Case

// validations/text.js
import {
	validateLength,
	validateFormat,
} from 'ember-changeset-validations/validators';

export default {
  text: {
    validateFormat({regex: /^[a-z]*$/ }),
    validateLength({max: 250, allowBlank: true})
  }
};

Steps to reproduce

type something, delete everything, will be isInvalid=true and trigger the validateFormat-error

Expected Behavior

expecting to be valid, because 1st regex demands zero or more (*) and 2nd allowBlank is set.

a normal js match would indeed find a blank string:

var str = '';
var re = /^[a-z]*$/;
var found = str.match(re);
console.log(found);
// output: ["", index: 0, input: ""]

Actual Behavior

isInvalid
(removing the string-boundaries ^ and $ doesn't change anything but of course make the regex obsolete)

ember-data errors support?

Does it make sense to have a validator similar to ember-cp-validations' ds-error validator? The main goal (for me) would be to consolidate the sources of error messages, but perhaps this would over-complicate the functionality intended here.

Otherwise, the user needs to manually handle ember-data (server) errors if changeset.save rejects.

Email validation issue

  • - I've raised a PR regarding the email validation, could you please review and merge?

#119

The issue is, email validation accepts without the domain (like test@gmail).

Version

Test Case

Steps to reproduce

Expected Behavior

Actual Behavior

Ember 1.13.13 support?

After reading through the GH template that pops up when creating an issue ... I'm uncertain if this is a bug/ support like question / or documentation clarification (improvement?). Regardless, I have reproduced it with twiddle and captured a quick/easy to watch animated gif showing the breakdown.

I decided to use this ember addon because it has support for 1.13.13. When I actually implemented this (following the twiddle shown in the README) I discovered an issue worth mentioning.

https://ember-twiddle.com/b70553450d0c7e4e2329e02b7d082e11

With ember 2x and the 2x series template compiler it works as desired. Validations fall off as you type ahead. But with ember 1.13.13 and the older template compiler you are required to add the ember-get-helper because without it you get an error.

If you run the twiddle above you will see the following

validations

This makes me think we have 1 of 2 issues

  1. the twiddle shows a legit bug in ember-changeset-validations (a possible conflict with ember 1.13.13)
  2. ember-get-helper is broken with ember 1.13.13 and this implementation of it (as shown in the README)

I wanted to raise awareness mostly so we can update the twiddle/ call out that this implementation may not work for 1.13.13 or find and fix the bug with ember-changeset-validations (or ember-get-helper)

Update

It's worth noting that the actual validations do fall off from the changset itself - it seems to be a visual issue only. I'd guess we either don't notifyPropertyChange correctly or the ember-get-helper isn't picking that notify up as it should

Update 2

After I removed the ember-get-helper altogether (by doing the below) I still see this behavior making me think it's between ember 1.13.13/the template compiler? and ember-changeset-validations

{{#if changeset.error.firstName}}
            <ul>
            {{#each changeset.error.firstName.validation as |message|}}
                <li><span>{{message}}</span></li>
            {{/each}}
            </ul>
{{/if}}

Update 3

Also found this in other related documentation and it's broken as well

{{#if changeset.error.firstName}}
  <p>{{changeset.error.firstName.validation}}</p>
{{/if}}

Validate type

Maybe if the underlying model is a DS.Model, we can check and enforce type as a validator

`buildMessage` function is backwards incompatible

Version

Latest Master starting at #126

Test Case

Steps to reproduce

Expected Behavior

From the Docs and existing behavior the buildMessage function should have a signature like this:
buildMessage(key, type, value, context = {}).

Actual Behavior

On this line the signature made a backwards incompatible change and now looks like this: buildMessage(key, result).

Is the next release intended to bump the major version? If not, any apps relying on buildMessage will be broken.

Possible to validate one field based on the value of another field?

I have a use case where I want to validate one field based on a value in another field (which, lets say, hasn't been changed).

Right now, using a custom validator, I can only see the validation arguments, field name, old value, new value, and change object. Is it possible to access the object underneath the changeset within a validation function?

Blueprint for new validator

  • Create validator with name as camelized function name
  • Add unit test for it
export default function validatorName(options = {}) {
  return (key, newValue, oldValue, changes) => {
    return true;
  };
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.