Skip to content

Instantly share code, notes, and snippets.

@infosniper
Forked from poteto/controllers.application.js
Last active September 19, 2017 09:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save infosniper/15d9df73e3be2c953731c99084a60a48 to your computer and use it in GitHub Desktop.
Save infosniper/15d9df73e3be2c953731c99084a60a48 to your computer and use it in GitHub Desktop.
ember-changeset-validations demo
// https://ember-twiddle.com/15d9df73e3be2c953731c99084a60a48?openFiles=controllers.application.js%2C
import Ember from 'ember';
import AdultValidations from '../validations/adult';
import ChildValidations from '../validations/child';
import { reservedEmails } from '../validators/uniqueness';
import { schema } from '../models/user';
const { get } = Ember;
const { keys } = Object;
export default Ember.Controller.extend({
AdultValidations,
ChildValidations,
reservedEmails,
actions: {
save(changeset) {
console.log('running action: save');
let snapshot = changeset.snapshot();
return changeset
.cast(keys(schema))
.validate()
.then(() => {
if (get(changeset, 'isValid')) {
changeset.execute();
changeset.rollback();
return
}
}).catch(() => {
changeset.restore(snapshot);
});
},
reset(changeset) {
console.log('running action: reset');
return changeset.rollback();
},
validateProperty(changeset, property) {
console.log('running action: validateProperty');
return changeset.validate(property);
}
}
});
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo, hasMany } from 'ember-data/relationships';
export const schema = {
firstName: attr('string'),
lastName: attr('string'),
email: attr('string'),
age: attr('number'),
job: attr('string')
};
export default Model.extend(schema);
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').createRecord('user', {
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@example.com',
age: 18,
job: 'worker'
});
}
});
{{marketing-copy reservedEmails=reservedEmails}}
<hr>
{{!--
Try changing `type` to `adult` and `validations` to `AdultValidations`
Try changing `type` to `child` and `validations` to `ChildValidations` o
--}}
{{edit-user
user=model
validations=AdultValidations
type="adult"
save=(action "save")
reset=(action "reset")
validateProperty=(action "validateProperty")
}}
<h4>
{{type}} user: <code>
{{#if user.firstName}}{{user.firstName}} {{user.lastName}}{{/if}}{{#if user.age}}, age {{user.age}}{{/if}}
{{#if user.job}} is a {{user.job}}{{/if}}
</code>
</h4>
{{#with (changeset user validations) as |changeset|}}
{{validated-field
type="text"
changeset=changeset
property="firstName"
validateProperty=(action validateProperty changeset "firstName")
}}
{{validated-field
type="text"
changeset=changeset
property="lastName"
validateProperty=(action validateProperty changeset "lastName")
}}
{{validated-field
type="text"
changeset=changeset
property="email"
validateProperty=(action validateProperty changeset "email")
}}
{{validated-field
type="text"
changeset=changeset
property="job"
validateProperty=(action validateProperty changeset "job")
}}
{{validated-field
type="number"
changeset=changeset
property="age"
validateProperty=(action validateProperty changeset "age")
}}
{{log changeset}}
___[changeset.changes]________________
<ul>
{{#each changeset.changes as |change|}}
<li>
{{change.key}}: {{get changeset._content change.key}} -> {{change.value}}
</li>
{{/each}}
</ul>
<div class="row">
<button {{action save changeset}} disabled={{changeset.isInvalid}} class="button-primary">Save</button>
<button {{action reset changeset}}>Reset</button>
</div>
{{/with}}
<iframe src="https://ghbtns.com/github-btn.html?user=DockYard&repo=ember-changeset&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=DockYard&repo=ember-changeset-validations&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe>
<p><code>ember-changeset</code> and <code>ember-changeset-validations</code> provide an easy way to validate and set changes to your data. Unlike CP or observer based validations, you don't need to write anything special to prevent data from immediately becoming invalid. Validators are simply functions, no CPs or observers required. Try clearing the inputs below, entering invalid values, or even just focusing out. Email is an async validation.</p>
<p>
Reserved emails: {{#each reservedEmails as |email|}}
<code>{{email}}</code>
{{/each}}
</p>
<label for={{concat type "-" property}}>{{property}}</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}}
{
"version": "0.12.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.12.0",
"ember-template-compiler": "2.12.0",
"ember-testing": "2.12.0",
"skeleton-css": "https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css"
},
"addons": {
"ember-data": "2.12.1",
"ember-changeset-validations": "1.2.6",
"ember-one-way-controls": "0.8.3"
}
}
import Ember from 'ember';
import { validatePresence,
validateLength,
validateNumber } from 'ember-changeset-validations/validators';
import UserValidations from './user';
const { assign } = Ember;
export default assign({}, UserValidations, {
age: validateNumber({ gte: 18, lte: 120 }),
lastName: [
validatePresence(true),
validateLength({ min: 2 })
],
job: validatePresence(true)
});
import Ember from 'ember';
import { validateNumber } from 'ember-changeset-validations/validators';
import UserValidations from './user';
const { assign } = Ember;
export default assign({}, UserValidations, {
age: validateNumber({ gt: 6, lt: 17 })
});
// 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} muas oeda oda gleich oid sei wia {gte}',
equalTo: '{description} must be equal to {is}',
lessThan: '{description} muas jinga sei ois {lt}',
lessThanOrEqualTo: '{description} muas jinga oda gleich oid sei wia {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'
}
import {
validatePresence,
validateLength,
validateFormat
} from 'ember-changeset-validations/validators';
import validateUniqueness from '../validators/uniqueness';
export default {
firstName: [
validatePresence(true),
validateLength({ min: 2 })
],
email: [
validateUniqueness(),
validateFormat({ type: 'email' })
]
}
import Ember from 'ember';
const { RSVP: { resolve } } = Ember;
export const reservedEmails = ['foo@bar.com', 'admin@example.com'];
export default function validateUniqueness() {
return (key, newValue, oldValue, changes) => {
let isAvailable = reservedEmails.indexOf(newValue) === -1;
return resolve(isAvailable || 'is taken');
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment