@@ -2,12 +2,10 @@ import React, { Component, PropTypes } from 'react';
2
2
import { connect } from 'react-redux' ;
3
3
import shallowEqual from '../utils/shallow-equal' ;
4
4
import _get from '../utils/get' ;
5
- import mapValues from '../utils/map-values' ;
6
5
import omit from '../utils/omit' ;
7
6
8
7
import actions from '../actions' ;
9
8
import getValidity from '../utils/get-validity' ;
10
- import invertValidity from '../utils/invert-validity' ;
11
9
import invertValidators from '../utils/invert-validators' ;
12
10
import isValidityInvalid from '../utils/is-validity-invalid' ;
13
11
import isValid from '../form/is-valid' ;
@@ -16,6 +14,7 @@ import getModel from '../utils/get-model';
16
14
import getField from '../utils/get-field' ;
17
15
import deepCompareChildren from '../utils/deep-compare-children' ;
18
16
import containsEvent from '../utils/contains-event' ;
17
+ import mergeValidity from '../utils/merge-validity' ;
19
18
import invariant from 'invariant' ;
20
19
21
20
const propTypes = {
@@ -134,7 +133,7 @@ function createFormClass(s = defaultStrategy) {
134
133
if ( this . props . getRef ) this . props . getRef ( node ) ;
135
134
}
136
135
137
- validate ( nextProps , initial = false ) {
136
+ validate ( nextProps , initial = false , submit = false ) {
138
137
const {
139
138
model,
140
139
dispatch,
@@ -162,12 +161,31 @@ function createFormClass(s = defaultStrategy) {
162
161
163
162
const validatorsChanged = validators !== this . props . validators
164
163
|| errors !== this . props . errors ;
164
+ const fieldKeys = ( validators ? Object . keys ( validators ) : [ ] )
165
+ . concat ( errors ? Object . keys ( errors ) : [ ] ) ;
165
166
166
167
const fieldsErrors = { } ;
167
168
let validityChanged = false ;
168
169
169
- // this is (internally) mutative for performance reasons.
170
- const validateField = ( errorValidator , field ) => {
170
+ const keysToValidate = [ ] ;
171
+
172
+ fieldKeys . forEach ( key => {
173
+ if ( ! ! ~ keysToValidate . indexOf ( key ) ) return ;
174
+
175
+ const valuesChanged = key === ''
176
+ ? modelValue !== nextProps . modelValue
177
+ : ( s . get ( modelValue , key ) !== s . get ( nextProps . modelValue , key ) ) ;
178
+
179
+ if ( submit || initial
180
+ || valuesChanged
181
+ || ( validators && ( this . props . validators [ key ] !== validators [ key ] ) )
182
+ || ( errors && ( this . props . errors [ key ] !== errors [ key ] ) )
183
+ || ! ! ~ key . indexOf ( '[]' ) ) {
184
+ keysToValidate . push ( key ) ;
185
+ }
186
+ } ) ;
187
+
188
+ const validateField = ( field , errorValidator ) => {
171
189
if ( ! ! ~ field . indexOf ( '[]' ) ) {
172
190
const [ parentModel , childModel ] = field . split ( '[]' ) ;
173
191
@@ -176,84 +194,82 @@ function createFormClass(s = defaultStrategy) {
176
194
: nextProps . modelValue ;
177
195
178
196
nextValue . forEach ( ( subValue , index ) => {
179
- validateField ( errorValidator , `${ parentModel } [${ index } ]${ childModel } ` ) ;
197
+ validateField ( `${ parentModel } [${ index } ]${ childModel } ` , errorValidator ) ;
180
198
} ) ;
181
199
} else {
182
200
const nextValue = field
183
201
? s . get ( nextProps . modelValue , field )
184
202
: nextProps . modelValue ;
185
203
186
- const currentValue = field
187
- ? s . get ( modelValue , field )
188
- : modelValue ;
189
-
190
204
const currentErrors = getField ( formValue , field ) . errors ;
205
+ const fieldErrors = getValidity ( errorValidator , nextValue ) ;
191
206
192
- // If the validators didn't change, the validity didn't change.
193
- if ( ( ! initial && ! validatorsChanged ) && ( nextValue === currentValue ) ) {
194
- fieldsErrors [ field ] = getField ( formValue , field ) . errors ;
195
- } else {
196
- const fieldErrors = getValidity ( errorValidator , nextValue ) ;
197
-
198
- if ( ! validityChanged && ! shallowEqual ( fieldErrors , currentErrors ) ) {
199
- validityChanged = true ;
200
- }
201
-
202
- fieldsErrors [ field ] = fieldErrors ;
207
+ if ( ! validityChanged && ! shallowEqual ( fieldErrors , currentErrors ) ) {
208
+ validityChanged = true ;
203
209
}
210
+
211
+ fieldsErrors [ field ] = mergeValidity ( fieldsErrors [ field ] , fieldErrors ) ;
204
212
}
205
213
} ;
206
214
207
- // Run errors first, validations should take precendence.
208
- // When run below will replace the contents of the fieldErrors[].
209
- mapValues ( errors , validateField ) ;
215
+ keysToValidate . forEach ( field => {
216
+ if ( validators && validators [ field ] ) {
217
+ validateField ( field , invertValidators ( validators [ field ] ) ) ;
218
+ }
219
+ if ( errors && errors [ field ] ) {
220
+ validateField ( field , errors [ field ] ) ;
221
+ }
222
+ } ) ;
210
223
211
224
if ( typeof validators === 'function' ) {
212
225
const nextValue = nextProps . modelValue ;
213
226
const currentValue = modelValue ;
214
227
215
- // If the validators didn't change, the validity didn't change.
216
- if ( ( ! initial && ! validatorsChanged ) && ( nextValue === currentValue ) ) {
217
- // TODO this will only set the errors on form when using the function.
218
- // How handle? Safe to assume will be no dispatch?
219
- // fieldsErrors[field] = getField(formValue, field).errors;
220
- } else {
221
- const multiFieldErrors = getValidity ( validators , nextValue ) ;
222
-
223
- if ( multiFieldErrors ) {
224
- Object . keys ( multiFieldErrors ) . forEach ( ( key ) => {
225
- // key will be the model value to apply errors to.
226
- const fieldErrors = multiFieldErrors [ key ] ;
227
- const currentErrors = getField ( formValue , key ) . errors ;
228
+ if ( ! submit && ( ! initial && ! validatorsChanged ) && ( nextValue === currentValue ) ) {
229
+ // If neither the validators nor the values have changed,
230
+ // the validity didn't change.
231
+ return ;
232
+ }
228
233
229
- // Invert validators
230
- Object . keys ( fieldErrors ) . forEach ( ( validationName ) => {
231
- fieldErrors [ validationName ] = ! fieldErrors [ validationName ] ;
232
- } ) ;
234
+ const multiFieldErrors = getValidity ( validators , nextValue ) ;
233
235
234
- if ( ! validityChanged && ! shallowEqual ( fieldErrors , currentErrors ) ) {
235
- validityChanged = true ;
236
- }
236
+ if ( multiFieldErrors ) {
237
+ Object . keys ( multiFieldErrors ) . forEach ( ( key ) => {
238
+ // key will be the model value to apply errors to.
239
+ const fieldErrors = multiFieldErrors [ key ] ;
240
+ const currentErrors = getField ( formValue , key ) . errors ;
237
241
238
- fieldsErrors [ key ] = fieldErrors ;
242
+ // Invert validators
243
+ Object . keys ( fieldErrors ) . forEach ( ( validationName ) => {
244
+ fieldErrors [ validationName ] = ! fieldErrors [ validationName ] ;
239
245
} ) ;
240
- }
241
- }
242
- } else if ( validators ) {
243
- const errorValidators = invertValidators ( validators ) ;
244
246
245
- mapValues ( errorValidators , validateField ) ;
247
+ if ( ! validityChanged && ! shallowEqual ( fieldErrors , currentErrors ) ) {
248
+ validityChanged = true ;
249
+ }
250
+
251
+ fieldsErrors [ key ] = mergeValidity ( fieldsErrors [ key ] , fieldErrors ) ;
252
+ } ) ;
253
+ }
246
254
}
247
255
248
256
// Compute form-level validity
249
- if ( ! fieldsErrors . hasOwnProperty ( '' ) ) {
257
+ if ( ! fieldsErrors . hasOwnProperty ( '' ) && ! ~ fieldKeys . indexOf ( '' ) ) {
250
258
fieldsErrors [ '' ] = false ;
251
259
validityChanged = validityChanged
252
260
|| isValidityInvalid ( formValue . $form . errors ) ;
253
261
}
254
262
255
263
if ( validityChanged ) {
256
- dispatch ( s . actions . setFieldsErrors ( model , fieldsErrors ) ) ;
264
+ dispatch ( s . actions . setFieldsErrors (
265
+ model ,
266
+ fieldsErrors ,
267
+ { merge : submit }
268
+ ) ) ;
269
+ }
270
+
271
+ if ( submit ) {
272
+ dispatch ( s . actions . addIntent ( model , { type : 'submit' } ) ) ;
257
273
}
258
274
}
259
275
@@ -317,13 +333,10 @@ function createFormClass(s = defaultStrategy) {
317
333
if ( e && ! this . props . action ) e . preventDefault ( ) ;
318
334
319
335
const {
320
- model,
321
336
modelValue,
322
337
formValue,
323
338
onSubmit,
324
- dispatch,
325
339
validators,
326
- errors : errorValidators ,
327
340
} = this . props ;
328
341
329
342
const formValid = formValue
@@ -336,49 +349,7 @@ function createFormClass(s = defaultStrategy) {
336
349
return modelValue ;
337
350
}
338
351
339
- let fieldsValidity = { } ;
340
-
341
- // this is (internally) mutative for performance reasons.
342
- const validateField = ( validator , field ) => {
343
- if ( ! ! ~ field . indexOf ( '[]' ) ) {
344
- const [ parentModel , childModel ] = field . split ( '[]' ) ;
345
-
346
- const fieldValue = parentModel
347
- ? s . get ( modelValue , parentModel )
348
- : modelValue ;
349
-
350
- fieldValue . forEach ( ( subValue , index ) => {
351
- validateField ( validator , `${ parentModel } [${ index } ]${ childModel } ` ) ;
352
- } ) ;
353
- } else {
354
- const fieldValue = field
355
- ? s . get ( modelValue , field )
356
- : modelValue ;
357
-
358
- const fieldValidity = getValidity ( validator , fieldValue ) ;
359
-
360
- fieldsValidity [ field ] = fieldValidity ;
361
- }
362
- } ;
363
-
364
- if ( typeof validators === 'function' ) {
365
- Object . assign ( fieldsValidity , validators ( modelValue ) ) ;
366
- } else {
367
- mapValues ( validators , validateField ) ;
368
- }
369
-
370
- fieldsValidity = invertValidity ( fieldsValidity ) ;
371
-
372
- mapValues ( errorValidators , validateField ) ;
373
-
374
- dispatch ( s . actions . batch ( model , [
375
- s . actions . setFieldsErrors (
376
- model ,
377
- fieldsValidity ,
378
- { merge : true }
379
- ) ,
380
- s . actions . addIntent ( model , { type : 'submit' } ) ,
381
- ] ) ) ;
352
+ this . validate ( this . props , false , true ) ;
382
353
383
354
return modelValue ;
384
355
}
0 commit comments