Skip to content

Commit

Permalink
Merge pull request #5 from formly-js/validation-messages
Browse files Browse the repository at this point in the history
Implemented validation messages.
  • Loading branch information
matt-sanders authored Feb 19, 2017
2 parents 964a43e + 7f9c8c1 commit 6fb7ddb
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 60 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-formly",
"version": "2.1.0",
"version": "2.2.0",
"description": "A simple and extendable form builder for Vue.js",
"main": "dist/vue-formly.js",
"scripts": {
Expand Down
17 changes: 15 additions & 2 deletions src/components/FormlyField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<script>
const Vue = require('vue');
import Util, {getTypes, setError} from '../util';
import Util, {getTypes, setError, parseValidationString} from '../util';
export default {
props: ['form', 'model', 'field', 'to'],
computed: {
Expand Down Expand Up @@ -38,9 +38,22 @@
if ( !this.field.required && !this.model[ this.field.key ] ) return;
let validator = this.field.validators[validKey];
let validatorMessage = false;
if ( typeof validator === 'object' ){
if ( !( 'message' in validator ) ){
console.error( "Looks like you've set a validator object without setting a message. If you don't need to explicity set the message just define the validator as either an expression or a function. Refer to the docs for more info");
} else {
validatorMessage = validator.message;
validator = validator.expression;
}
}
let label = ( 'templateOptions' in this.field ) && ( 'label' in this.field.templateOptions ) ? this.field.templateOptions.label : '';
validatorMessage = parseValidationString( validKey, validatorMessage, label, model[ this.field.key ] );
let valid = typeof validator == 'function' ? !validator(field, model) : !eval(validator);
setError(this.form, this.field.key, validKey, valid);
setError(this.form, this.field.key, validKey, valid, validatorMessage);
});
}
Expand Down
27 changes: 14 additions & 13 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import Components from './components/index';
import Filters from './filters/index';
import Util,{getTypes, addType} from './util';
import Util,{getTypes, addType, addValidationMessage} from './util';


let Formly = {
getTypes,
addType,
install(Vue, options){

//install our components
Components(Vue);
Filters(Vue);
getTypes,
addType,
addValidationMessage,
install(Vue, options){

//install our components
Components(Vue);
Filters(Vue);

Vue.$formly = {getTypes, addType};
}
Vue.$formly = {getTypes, addType, addValidationMessage};
}
};

//auto install
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(Formly);
//expose formly functions if auto installed
window.Vue.$formly = {getTypes, addType};
window.Vue.use(Formly);
//expose formly functions if auto installed
window.Vue.$formly = {getTypes, addType, addValidationMessage};
}
export default Formly;
43 changes: 37 additions & 6 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const exports = {
formlyFields: {}
formlyFields: {},
validationMessages: {}
};
export default exports;

Expand All @@ -11,11 +12,11 @@ export default exports;
* @param {Object} options
*/
export function addType(id, options){
exports.formlyFields['formly_'+id] = options;
exports.formlyFields['formly_'+id] = options;
}

export function getTypes(){
return exports.formlyFields;
return exports.formlyFields;
}

/**
Expand All @@ -25,7 +26,37 @@ export function getTypes(){
* @param {String} err
* @param {Bool} isError
*/
export function setError(form, key, err, isError){
if ( !form.$errors[key] ) form.$errors[key] = {};
form.$errors[key][err] = isError;
export function setError(form, key, err, isError, message = false){
if ( !form.$errors[key] ) form.$errors[key] = {};
form.$errors[key][err] = isError ? message || isError : false;
}

/**
* Adds a validation string to Vue Formly to be used later on by validations
* @param {string} key
* @param {string} message
*/
export function addValidationMessage(key, message){
exports.validationMessages[ key ] = message;
}

/**
* Given a message key or message it parses in the label and the value
* @param {string/bool} key
* @param {string} message
* @param {string} label
* @param {string} value
*/
export function parseValidationString(key, message, label, value){

// if a key has been passed and there's no validation message and no message has been passed then return
if ( key && !(key in exports.validationMessages ) && !message ) return false;

// first check if a validation message with this key exists
if ( key in exports.validationMessages ){
message = exports.validationMessages[key];
}

let output = message.replace(/\%l/g, label).replace(/\%v/g, value);
return output;
}
109 changes: 109 additions & 0 deletions test/unit/specs/FormlyField.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import chai from 'chai';
const expect = chai.expect;
import Vue from 'vue';
import FormlyField from 'src/components/FormlyField.vue';
import Utils from 'src/util';

let el, vm;

Expand Down Expand Up @@ -284,6 +285,114 @@ describe('FormlyField', () => {
done();
},0);
});

describe("Validation Messages", (done) => {

//what do we want to do

//--- define messages ---
//vf.addValidationString('validatorKey', 'message');

//add specific messages
/*
* validators: {
* test: {
* expression: function(){},
* message: '%l requires 10, you entered %s'
* }
* }
*/

it('Inline messages', () => {
let data = {
form: {
$valid: true,
$errors: {}
},
model: {
search: 'testing'
},
fields: [
{
key: 'search',
type: 'test',
validators: {
validatorMessage:
{
expression: 'model.search == "test"',
message: 'Must equal test'
}
}
}
]
};

createValidField(data);
expect(vm.form.$errors.search.validatorMessage).to.equal('Must equal test');
});

it('Inline messages with values parsed', () => {
let data = {
form: {
$valid: true,
$errors: {}
},
model: {
search: 'testing'
},
fields: [
{
key: 'search',
type: 'test',
templateOptions: {
label: 'test'
},
validators: {
validatorMessage:
{
expression: 'model.search == "test"',
message: '%l and %v'
}
}
}
]
};

createValidField(data);
expect(vm.form.$errors.search.validatorMessage).to.equal('test and testing');
});

it('Global messages with values parsed', () => {
let data = {
form: {
$valid: true,
$errors: {}
},
model: {
search: 'testing'
},
fields: [
{
key: 'search',
type: 'test',
templateOptions: {
label: 'test'
},
validators: {
validatorMessage: 'model.search == "test"',
}
}
]
};

//just mock the other vue functions
Utils.validationMessages.validatorMessage = '%l and %v';

createValidField(data);
expect(vm.form.$errors.search.validatorMessage).to.equal('test and testing');
});

});

});

Expand Down
102 changes: 64 additions & 38 deletions test/unit/specs/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,73 @@
import {expect} from 'chai';

import VueFormly from 'src/index';
import Util, {addType, getTypes, setError} from 'src/util';
import Util, {addType, getTypes, setError, addValidationMessage, parseValidationString} from 'src/util';


describe('module', () => {

it('module props', () => {
expect(VueFormly).to.exist;
expect(VueFormly.install).to.be.a('function');
expect(VueFormly.addType).to.be.a('function');
expect(VueFormly.getTypes).to.be.a('function');
expect(addType).to.be.a('function');
expect(getTypes).to.be.a('function');
expect(setError).to.be.a('function');
expect(Util.formlyFields).to.be.a('object');
});

it('should add fields to Vue', () => {

//mock vue
window.Vue = {
component(){},
filter(){}
};
VueFormly.install(Vue);

let newField = {
foo: 'bar'
};
addType('test', newField);
expect(getTypes().formly_test).to.deep.equal(newField);
});

it('should handle errors',()=>{
let form = {
$errors: {}
};
setError(form, 'fname', 'required', true);
expect(form.$errors.fname.required).to.be.true;

setError(form, 'fname', 'required', false);
expect(form.$errors.fname.required).to.be.false;
});
it('module props', () => {
expect(VueFormly).to.exist;
expect(VueFormly.install).to.be.a('function');
expect(VueFormly.addType).to.be.a('function');
expect(VueFormly.getTypes).to.be.a('function');
expect(VueFormly.addValidationMessage).to.be.a('function');
expect(addType).to.be.a('function');
expect(getTypes).to.be.a('function');
expect(setError).to.be.a('function');
expect(Util.formlyFields).to.be.a('object');
});

it('addType()', () => {

//mock vue
window.Vue = {
component(){},
filter(){}
};
VueFormly.install(Vue);

let newField = {
foo: 'bar'
};
addType('test', newField);
expect(getTypes().formly_test).to.deep.equal(newField);
});

it('setError()',()=>{
let form = {
$errors: {}
};
setError(form, 'fname', 'required', true);
expect(form.$errors.fname.required).to.be.true;

setError(form, 'fname', 'required', false);
expect(form.$errors.fname.required).to.be.false;

setError(form, 'fname', 'required', true, 'Hey there');
expect(form.$errors.fname.required).to.equal('Hey there');

setError(form, 'fname', 'required', false, 'Hey there');
expect(form.$errors.fname.required).to.be.false;
});

it('addValidationMessage()', () => {
addValidationMessage('test', 'testing');
expect(Util.validationMessages.test).to.equal('testing');
});

it('parseValidationString()', () => {
addValidationMessage('parseTest', '%l just %v testing');
let message = '%l just %v testing';
let expected = 'label just value testing';
let output = parseValidationString('parseTest', false, 'label', 'value');
expect(output).to.equal(expected);

output = parseValidationString(false, message, 'label', 'value');
expect(output).to.equal(expected);

output = parseValidationString('blahblahblah', false, '', '');
expect(output).to.be.false;
});

});

0 comments on commit 6fb7ddb

Please sign in to comment.