Skip to content

Commit 05c392c

Browse files
authored
Merge pull request #8 from formly-js/validate_promises
Validate promises
2 parents 0a18b45 + 677ba9a commit 05c392c

File tree

6 files changed

+200
-85
lines changed

6 files changed

+200
-85
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ flycheck_*.el
1111

1212
node_modules/
1313
coverage/
14-
test/unit/coverage
14+
test/unit/coverage
15+
16+
shrinkwrap.yaml

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: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,54 @@
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+
this.$refs[ field.key ][0].validate()
18+
.then(()=>{
19+
count++;
20+
if( target == count ) resolve();
21+
});
22+
});
23+
});
24+
}
25+
},
26+
props: ['form', 'model', 'fields'],
27+
created(){
1228
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-
});
29+
//make sure that the 'value' is always set
30+
this.fields.forEach( field => {
31+
if ( typeof this.model[ field.key ] == 'undefined' ) this.$set(this.model, field.key, '');
32+
});
1733
18-
19-
//set our validation options
20-
this.$set(this.form, '$errors', {});
21-
this.$set(this.form, '$valid', true);
34+
35+
//set our validation options
36+
this.$set(this.form, '$errors', {});
37+
this.$set(this.form, '$valid', true);
2238
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-
}
39+
this.$watch('form.$errors', function(val){
40+
let valid = true;
41+
Object.keys(this.form.$errors).forEach((key)=>{
42+
let errField = this.form.$errors[key];
43+
Object.keys(errField).forEach((errKey) => {
44+
if ( errField[errKey] ) valid = false;
45+
})
46+
});
47+
this.form.$valid = valid;
48+
}, {
49+
deep: true
50+
});
51+
52+
}
53+
}
3854
</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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,60 @@ 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+
validTest2: {
171+
$dirty: false
172+
}
173+
},
174+
model: {validTest:'', validTest2: ''},
175+
fields: [{
176+
key: 'validTest',
177+
type: 'input'
178+
},
179+
{
180+
key: 'validTest2',
181+
type: 'input'
182+
}]
183+
};
184+
185+
el = document.createElement('DIV');
186+
document.body.appendChild(el);
187+
Vue.component('formly-field', ValidField);
188+
vm = new Vue({
189+
data: data,
190+
template: '<formly-form :form="form" :model="model" :fields="fields"></formly-form>'
191+
}).$mount(el);
192+
193+
194+
let spy = sinon.spy();
195+
196+
let prom = vm.$children[0].validate();
197+
prom.then(()=>spy());
198+
setTimeout(()=>{
199+
spy.should.be.calledOnce;
200+
formlyFieldSpy.should.be.calledTwice;
201+
done();
202+
});
203+
});
149204
});
150205

webpack.build.config.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@ module.exports = [
2525
libraryTarget: "umd"
2626
},
2727

28-
plugins: [
29-
new webpack.optimize.DedupePlugin(),
28+
plugins: [
3029
new webpack.DefinePlugin({
3130
'process.env' : {
3231
NODE_ENV : JSON.stringify('production')
3332
}
3433
}),
34+
new webpack.optimize.UglifyJsPlugin({
35+
compress: {
36+
warnings: false
37+
}
38+
}),
39+
new webpack.optimize.DedupePlugin(),
3540
new webpack.BannerPlugin(banner, {
3641
raw: true
3742
}),
@@ -81,7 +86,7 @@ module.exports = [
8186
],
8287

8388
module: {
84-
loaders: loaders
89+
loaders: loaders
8590
},
8691

8792
resolve: {

0 commit comments

Comments
 (0)