Skip to content

Commit 97eef09

Browse files
committed
working on promise based validation
1 parent 0a18b45 commit 97eef09

File tree

4 files changed

+194
-81
lines changed

4 files changed

+194
-81
lines changed

src/components/FormlyField.vue

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -15,65 +15,69 @@
1515
methods: {
1616
validate:function(){
1717
18-
//first check if we need to create a field
19-
if ( !this.form.$errors[this.field.key] ) this.$set(this.form.$errors, this.field.key, {});
20-
if ( !this.field.templateOptions ) this.$set(this.field, 'templateOptions', {});
21-
22-
//check for required fields. This whole setting,unsetting thing seems kind of wrong though..
23-
//there might be a more 'vue-ey' way to do this...
24-
if ( this.field.required ){
25-
if ( !this.form.$errors[this.field.key].required ) this.$set(this.form.$errors[ this.field.key ], 'required', true);
26-
setError(this.form, this.field.key, 'required', !this.model[ this.field.key ]) ;
27-
}
28-
29-
//if we've got nothing left then return
30-
if ( !this.field.validators ) return;
31-
32-
//set these for the validators so we don't have to use 'this' in them
33-
let model = this.model;
34-
let field = this.field;
35-
36-
Object.keys(this.field.validators).forEach((validKey) => {
37-
if ( !this.form.$errors[this.field.key][validKey] ) this.$set(this.form.$errors[ this.field.key ], validKey, false);
38-
if ( !this.field.required && !this.model[ this.field.key ] ) {
39-
setError(this.form, this.field.key, validKey, false);
40-
return;
18+
return new Promise((resolve, reject)=>{
19+
//first check if we need to create a field
20+
if ( !this.form.$errors[this.field.key] ) this.$set(this.form.$errors, this.field.key, {});
21+
if ( !this.field.templateOptions ) this.$set(this.field, 'templateOptions', {});
22+
23+
//check for required fields. This whole setting,unsetting thing seems kind of wrong though..
24+
//there might be a more 'vue-ey' way to do this...
25+
if ( this.field.required ){
26+
if ( !this.form.$errors[this.field.key].required ) this.$set(this.form.$errors[ this.field.key ], 'required', true);
27+
setError(this.form, this.field.key, 'required', !this.model[ this.field.key ]) ;
4128
}
29+
30+
//if we've got nothing left then return
31+
if ( !this.field.validators ) return resolve();
4232
43-
let validator = this.field.validators[validKey];
44-
let validatorMessage = false;
33+
//set these for the validators so we don't have to use 'this' in them
34+
let model = this.model;
35+
let field = this.field;
36+
37+
Object.keys(this.field.validators).forEach((validKey) => {
38+
if ( !this.form.$errors[this.field.key][validKey] ) this.$set(this.form.$errors[ this.field.key ], validKey, false);
39+
if ( !this.field.required && !this.model[ this.field.key ] ) {
40+
setError(this.form, this.field.key, validKey, false);
41+
return resolve();
42+
}
4543
46-
if ( typeof validator === 'object' ){
47-
if ( !( 'message' in validator ) ){
48-
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");
49-
} else {
50-
validatorMessage = validator.message;
51-
validator = validator.expression;
44+
let validator = this.field.validators[validKey];
45+
let validatorMessage = false;
46+
47+
if ( typeof validator === 'object' ){
48+
if ( !( 'message' in validator ) ){
49+
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");
50+
} else {
51+
validatorMessage = validator.message;
52+
validator = validator.expression;
53+
}
5254
}
53-
}
54-
55-
let label = ( 'templateOptions' in this.field ) && ( 'label' in this.field.templateOptions ) ? this.field.templateOptions.label : '';
56-
validatorMessage = parseValidationString( validKey, validatorMessage, label, model[ this.field.key ] );
55+
56+
let label = ( 'templateOptions' in this.field ) && ( 'label' in this.field.templateOptions ) ? this.field.templateOptions.label : '';
57+
validatorMessage = parseValidationString( validKey, validatorMessage, label, model[ this.field.key ] );
5758
58-
let valid = false;
59-
if ( typeof validator === 'function' ){
60-
//set the asynchronous flag so that we know it's going
61-
let asyncKey = '$async_'+validKey;
62-
this.$set(this.form.$errors[ this.field.key ], asyncKey, true);
63-
64-
// setup for async validation
65-
validator(field, model, (asyncValid = false, asyncValidatorMessage = validatorMessage) => {
66-
// whenever validation is done via a function we will assume it's asynchronous and will require next() to be called
67-
// this way it doesn't matter if it's async or not, next() should always be called
68-
setError(this.form, this.field.key, validKey, !asyncValid, asyncValidatorMessage);
69-
this.$set(this.form.$errors[ this.field.key ], asyncKey, false);
70-
});
71-
} else {
72-
let res = new Function('model', 'field', 'return '+validator+';' );
73-
valid = !res.call({}, model, field);
74-
setError(this.form, this.field.key, validKey, valid, validatorMessage);
75-
}
76-
59+
let valid = false;
60+
if ( typeof validator === 'function' ){
61+
//set the asynchronous flag so that we know it's going
62+
let asyncKey = '$async_'+validKey;
63+
this.$set(this.form.$errors[ this.field.key ], asyncKey, true);
64+
65+
// setup for async validation
66+
validator(field, model, (asyncValid = false, asyncValidatorMessage = validatorMessage) => {
67+
// whenever validation is done via a function we will assume it's asynchronous and will require next() to be called
68+
// this way it doesn't matter if it's async or not, next() should always be called
69+
setError(this.form, this.field.key, validKey, !asyncValid, asyncValidatorMessage);
70+
this.$set(this.form.$errors[ this.field.key ], asyncKey, false);
71+
resolve();
72+
});
73+
} else {
74+
let res = new Function('model', 'field', 'return '+validator+';' );
75+
valid = !res.call({}, model, field);
76+
setError(this.form, this.field.key, validKey, valid, validatorMessage);
77+
resolve();
78+
}
79+
80+
});
7781
});
7882
}
7983
},

src/components/FormlyForm.vue

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,52 @@
11
<template>
22
<fieldset>
3-
<formly-field v-for="field in fields" :form.sync="form" :model.sync="model" :field="field"></formly-field>
3+
<formly-field :ref="field.key" v-for="field in fields" :form.sync="form" :model.sync="model" :field="field"></formly-field>
44
<slot></slot>
55
</fieldset>
66
</template>
77

88
<script>
9-
export default {
10-
props: ['form', 'model', 'fields'],
11-
created(){
9+
export default {
10+
methods: {
11+
validate(){
12+
return new Promise((resolve, reject) => {
13+
let target = this.fields.length;
14+
let count = 0;
15+
this.fields.forEach( field => {
16+
this.$set( this.form[ field.key ], '$dirty', true );
17+
console.log(this.$refs[ field.key ][0]);
18+
//this.$refs[ field.key ][0].validate();
19+
});
20+
resolve();
21+
});
22+
}
23+
},
24+
props: ['form', 'model', 'fields'],
25+
created(){
1226
13-
//make sure that the 'value' is always set
14-
this.fields.forEach( field => {
15-
if ( typeof this.model[ field.key ] == 'undefined' ) this.$set(this.model, field.key, '');
16-
});
27+
//make sure that the 'value' is always set
28+
this.fields.forEach( field => {
29+
if ( typeof this.model[ field.key ] == 'undefined' ) this.$set(this.model, field.key, '');
30+
});
1731
18-
19-
//set our validation options
20-
this.$set(this.form, '$errors', {});
21-
this.$set(this.form, '$valid', true);
32+
33+
//set our validation options
34+
this.$set(this.form, '$errors', {});
35+
this.$set(this.form, '$valid', true);
2236
23-
this.$watch('form.$errors', function(val){
24-
let valid = true;
25-
Object.keys(this.form.$errors).forEach((key)=>{
26-
let errField = this.form.$errors[key];
27-
Object.keys(errField).forEach((errKey) => {
28-
if ( errField[errKey] ) valid = false;
29-
})
30-
});
31-
this.form.$valid = valid;
32-
}, {
33-
deep: true
34-
});
35-
36-
}
37-
}
37+
this.$watch('form.$errors', function(val){
38+
let valid = true;
39+
Object.keys(this.form.$errors).forEach((key)=>{
40+
let errField = this.form.$errors[key];
41+
Object.keys(errField).forEach((errKey) => {
42+
if ( errField[errKey] ) valid = false;
43+
})
44+
});
45+
this.form.$valid = valid;
46+
}, {
47+
deep: true
48+
});
49+
50+
}
51+
}
3852
</script>

test/unit/specs/FormlyField.spec.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ const expect = chai.expect;
33
import Vue from 'vue';
44
import FormlyField from 'src/components/FormlyField.vue';
55
import Utils from 'src/util';
6+
import sinon from 'sinon';
7+
import sinonChai from 'sinon-chai';
8+
chai.use(sinonChai);
69

710
let el, vm;
811

@@ -167,6 +170,36 @@ describe('FormlyField', () => {
167170
return createForm('<formly-field :form.sync="form" :field="fields[0]" :model="model"></formly-field>', data);
168171
};
169172

173+
it('should return a promise', (done)=>{
174+
let data = {
175+
form: {
176+
$valid: true,
177+
$errors: {}
178+
},
179+
model: {
180+
search: ''
181+
},
182+
fields: [
183+
{
184+
key: 'search',
185+
type: 'test',
186+
required: true
187+
}
188+
]
189+
};
190+
createValidField(data);
191+
let cb = sinon.spy();
192+
193+
let prom = vm.$children[0].validate();
194+
expect(typeof prom.then).to.equal('function');
195+
196+
prom.then(()=>cb());
197+
setTimeout(()=>{
198+
cb.should.be.called;
199+
done();
200+
});
201+
});
202+
170203
it('should handle required values', (done) => {
171204

172205
let data = {

test/unit/specs/FormlyForm.spec.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,67 @@ describe('FormlyForm', () => {
146146
done();
147147
});
148148
});
149+
150+
it('validate()', (done)=>{
151+
let formlyFieldSpy = sinon.spy();
152+
let ValidField = Vue.extend({
153+
template: '<h1>ValidationField</h1>',
154+
props: ['form', 'model', 'field'],
155+
methods: {
156+
validate(){
157+
return new Promise(function(resolve, reject){
158+
formlyFieldSpy();
159+
resolve();
160+
});
161+
}
162+
}
163+
});
164+
165+
let data = {
166+
form: {
167+
validTest: {
168+
$dirty: false
169+
}
170+
},
171+
model: {validTest:''},
172+
fields: [{
173+
key: 'validTest',
174+
type: 'input'
175+
}]
176+
};
177+
178+
el = document.createElement('DIV');
179+
document.body.appendChild(el);
180+
vm = new Vue({
181+
data: data,
182+
template: '<formly-form :form="form" :model="model" :fields="fields"></formly-form>',
183+
components: {
184+
'formly-field': ValidField
185+
}
186+
}).$mount(el);
187+
188+
189+
let spy = sinon.spy();
190+
191+
let prom = vm.$children[0].validate();
192+
prom.then(()=>{
193+
console.log('prom.then');
194+
})
195+
.catch((e)=>{
196+
console.log('prom.catch', e);
197+
});
198+
return done();
199+
expect(typeof prom.then).to.equal('function');
200+
prom
201+
.then(()=>spy())
202+
.catch(()=>{
203+
204+
});
205+
return done();
206+
setTimeout(()=>{
207+
spy.should.be.called;
208+
done();
209+
});
210+
});
149211
});
150212

0 commit comments

Comments
 (0)