1
1
const Validator = require ( "validatorjs" ) ;
2
- import { IReactComponent , IOptions , IInputErrors } from "./types/ReactTypes " ;
2
+ import { IReactComponent , IOptions , IValidatorErrors , IDynamicKeyValues , ReactFormSubmitEventHandler } from "./specs/react-form-input-validator.spec " ;
3
3
4
- class ReactFormValidator {
4
+ class ReactFormValidator extends EventTarget {
5
5
private component : IReactComponent ;
6
- private submitCallback : Function ;
7
6
private rules : object = { } ;
8
- private errors : IInputErrors = { } ;
7
+ private errors : IValidatorErrors = { } ;
8
+
9
+ /**
10
+ * Event registered to notify the onreactformsubmit in {@link ReactFormValidator}.
11
+ * @returns A callback function {@link ReactFormSubmitEventHandler}.
12
+ * @example
13
+ * ```js
14
+ *
15
+ * // Refer "ReactFormValidator Interface" for react input form validator object creation
16
+ *
17
+ * this.form.addEventListener("onreactformsubmit", (fields) => {
18
+ * // Make your ajax calls here.
19
+ * });
20
+ * // or
21
+ * this.form.onreactformsubmit = (fields) => {
22
+ * // Make your ajax calls here.
23
+ * }
24
+ * ```
25
+ */
26
+ public onreactformsubmit : ReactFormSubmitEventHandler ;
9
27
10
28
/**
11
29
* Construct the React Input Form Validator instance.
@@ -17,23 +35,18 @@ class ReactFormValidator {
17
35
* @param options {@link Options }
18
36
* @example
19
37
* ```js
20
- * let form = new ReactFormValidator(this,
21
- * {
22
- * email: "required|email",
23
- * },
24
- * (fields) => {
25
- * // Make ajax request here to send data to server
26
- * },
38
+ *
39
+ * // Recommanded to do this in constructor of the form component.
40
+ * this.form = new ReactFormValidator(this,
27
41
* {
28
42
* locale: 'en'
29
43
* })
30
44
* ```
31
45
*/
32
- constructor ( component : IReactComponent , rules : object , callback : Function , options ?: IOptions ) {
46
+ constructor ( component : IReactComponent , options ?: IOptions ) {
47
+ super ( ) ;
33
48
ReactFormValidator . useLang ( ( options && options . locale ) ? options . locale : "en" ) ;
34
49
this . component = component ;
35
- this . rules = rules ;
36
- this . submitCallback = callback ;
37
50
this . handleFieldsChange = this . handleFieldsChange . bind ( this ) ;
38
51
this . handleSubmit = this . handleSubmit . bind ( this ) ;
39
52
this . handleBlurEvent = this . handleBlurEvent . bind ( this ) ;
@@ -45,9 +58,8 @@ class ReactFormValidator {
45
58
* @param locale string
46
59
* @example
47
60
* ```js
48
- * import ReactInputFormValidator from "react-input-form-validator";
49
61
*
50
- * ReactInputFormValidator .useLang("en");
62
+ * ReactFormValidator .useLang("en");
51
63
* ```
52
64
*/
53
65
static useLang ( locale : string ) : void {
@@ -63,7 +75,8 @@ class ReactFormValidator {
63
75
* :attribute inside errorMessage will be replaced with the attribute name.
64
76
* @example
65
77
* ```js
66
- * ReactInputFormValidator.register('telephone', function(value, requirement, attribute) {
78
+ *
79
+ * ReactFormValidator.register('telephone', function(value, requirement, attribute) {
67
80
* return value.match(/^\d{3}-\d{3}-\d{4}$/);
68
81
* }, 'The :attribute phone number is not in the format XXX-XXX-XXXX.');
69
82
*
@@ -73,6 +86,26 @@ class ReactFormValidator {
73
86
Validator . register ( name , callbackFn , errorMessage ) ;
74
87
}
75
88
89
+ /**
90
+ * Register an asynchronous rule which accepts a passes callback
91
+ *
92
+ * @param name The name of the rule.
93
+ * @param callbackFn
94
+ * @example
95
+ * ```js
96
+ *
97
+ * Validator.registerAsync('username_available', function(username, attribute, req, passes) {
98
+ * // do your database/api checks here etc
99
+ * // then call the `passes` method where appropriate:
100
+ * passes(); // if username is available
101
+ * passes(false, 'Username has already been taken.'); // if username is not available
102
+ * });
103
+ * ```
104
+ */
105
+ static registerAsync ( name : string , callbackFn : Function ) : void {
106
+ Validator . registerAsync ( name , callbackFn ) ;
107
+ }
108
+
76
109
/**
77
110
* You can also add your own custom language by calling setMessages:
78
111
*
@@ -81,7 +114,7 @@ class ReactFormValidator {
81
114
* @example
82
115
* ```js
83
116
*
84
- * ReactInputFormValidator .setMessages('lang_code', {
117
+ * ReactFormValidator .setMessages('lang_code', {
85
118
* required: 'The :attribute field is required.'
86
119
* });
87
120
* ```
@@ -95,7 +128,8 @@ class ReactFormValidator {
95
128
* @param name The name of the rule
96
129
* @example
97
130
* ```js
98
- * ReactInputFormValidator.getMessages('lang_code');
131
+ *
132
+ * ReactFormValidator.getMessages('lang_code');
99
133
* ```
100
134
*/
101
135
static getMessages ( name : string ) : object {
@@ -106,31 +140,65 @@ class ReactFormValidator {
106
140
* Get the default language being used
107
141
* @example
108
142
* ```js
109
- * ReactInputFormValidator.getDefaultLang(); // returns e.g. 'en'
143
+ *
144
+ * ReactFormValidator.getDefaultLang(); // returns e.g. 'en'
110
145
* ```
111
146
*/
112
147
static getDefaultLang ( ) : string {
113
148
return Validator . getDefaultLang ( ) ;
114
149
}
115
150
151
+ /**
152
+ * You can supply global custom attribute names in your app with the attributes property.
153
+ *
154
+ * @param callbackFn A Callback function to configure the attribute name.
155
+ * @example
156
+ * ```js
157
+ *
158
+ * ReactFormValidator.setAttributeFormatter(function(attribute) {
159
+ * return attribute.replace(/_/g, ' ');
160
+ * });
161
+ * ```
162
+ */
163
+ static setAttributeFormatter ( callbackFn : Function ) : void {
164
+ Validator . setAttributeFormatter ( callbackFn ) ;
165
+ }
166
+
167
+ /**
168
+ * Set the validation rules for form fields.
169
+ * @param rules The rules to validate.
170
+ * @example
171
+ * ```js
172
+ *
173
+ * this.form.useRules({
174
+ * email: "required|email",
175
+ * password: "required"
176
+ * })
177
+ * ```
178
+ */
179
+ public useRules ( rules ) : void {
180
+ this . rules = rules ;
181
+ }
182
+
116
183
/**
117
184
* Handle onchange event for input fields.
118
185
*
119
186
* @example
120
187
* ```js
188
+ *
121
189
* // Refer "ReactFormValidator Interface" for react input form validator object creation
122
190
*
123
- * <input name="email" onChange={form.handleFieldsChange} value={this.state.fields.email}>
191
+ * <input name="email" onChange={this. form.handleFieldsChange} value={this.state.fields.email}>
124
192
* ```
125
193
*/
126
194
public handleFieldsChange ( event ) {
127
- const name = event . target . name ;
195
+ const name : string = event . target . name ;
128
196
if ( this . component && name ) {
129
197
const fields = Object . assign ( { } , this . component . state . fields ) ;
130
198
fields [ name ] = ( event . target . type === "checkbox" ) ? this . getCheckboxValues ( event . target ) :
131
199
( event . target . type === "radio" ) ? this . getRadioButtonValues ( event . target ) :
132
200
event . target . value ;
133
- this . component . setState ( { fields : fields , isOwnUpdate : true } ) ;
201
+ this . component . setState ( { fields : fields , isValidatorUpdate : true } ) ;
134
202
}
135
203
}
136
204
@@ -140,6 +208,7 @@ class ReactFormValidator {
140
208
* @param event onsubmit event
141
209
* @example
142
210
* ```js
211
+ *
143
212
* // Refer "ReactFormValidator Interface" for react input form validator object creation
144
213
*
145
214
* <form onSubmit={form.handleSubmit}>
@@ -148,8 +217,8 @@ class ReactFormValidator {
148
217
*/
149
218
public handleSubmit ( event ) {
150
219
event . preventDefault ( ) ;
151
- if ( this . validateForm ( event . target ) && this . submitCallback ) {
152
- this . submitCallback ( this . component . state . fields ) ;
220
+ if ( this . validateForm ( event . target ) ) {
221
+ this . dispatchEvent ( this . getEvent ( this . component . state . fields ) ) ;
153
222
}
154
223
}
155
224
@@ -159,6 +228,7 @@ class ReactFormValidator {
159
228
* @param event onblur event
160
229
* @example
161
230
* ```js
231
+ *
162
232
* // Refer "ReactFormValidator Interface" for react input form validator object creation
163
233
* <input
164
234
* name="email"
@@ -170,21 +240,11 @@ class ReactFormValidator {
170
240
*/
171
241
public handleBlurEvent ( event ) {
172
242
const element : HTMLInputElement = event . target ;
173
-
174
- const initValidator = ( ) => {
175
- const inputErrors = this . validate ( element ) ;
176
- if ( this . component && this . component . hasOwnProperty ( "state" ) ) {
177
- this . errors = this . getErrorMessage ( inputErrors as Array < any > ,
178
- this . component . state . inputErrors ? this . component . state . inputErrors : { } ) ;
179
- this . component . setState ( { inputErrors : this . errors } ) ;
180
- }
181
- } ;
182
-
183
- if ( ( element . dataset !== undefined && element . dataset . hasOwnProperty ( "datepicker" ) ) ||
184
- element . getAttribute ( "data-datepicker" ) ) {
185
- setTimeout ( initValidator , 200 ) ;
186
- } else {
187
- initValidator ( ) ;
243
+ const inputErrors = this . validate ( element ) ;
244
+ if ( this . component && this . component . hasOwnProperty ( "state" ) ) {
245
+ this . errors = this . getErrorMessage ( inputErrors as Array < any > ,
246
+ this . component . state . errors ? this . component . state . errors : { } ) ;
247
+ this . component . setState ( { errors : this . errors , isValidatorUpdate : true } ) ;
188
248
}
189
249
}
190
250
@@ -196,27 +256,28 @@ class ReactFormValidator {
196
256
private validateForm ( form ) : boolean {
197
257
if ( ! this . component || ! this . component . state ) {
198
258
this . component . state = {
199
- inputErrors : { }
259
+ errors : { }
200
260
} ;
201
261
}
202
262
203
- form . querySelectorAll ( "textarea,select,input:not([type='submit']):not([type='file']):not([data-ignore-validation]):not([data-unique]) " )
263
+ form . querySelectorAll ( "textarea,select,input:not([type='submit']):not([type='file']):not([data-ignore-validation])" )
204
264
. forEach ( ( elem ) => {
205
265
const inputErrors = this . validate ( elem ) ;
206
- Object . assign ( this . component . state . inputErrors ,
207
- this . getErrorMessage ( inputErrors as Array < any > , this . component . state . inputErrors ) ) ;
266
+ Object . assign ( this . component . state . errors ,
267
+ this . getErrorMessage ( inputErrors as Array < any > , this . component . state . errors ) ) ;
208
268
} ) ;
209
269
210
270
this . component . setState ( {
211
- inputErrors : this . component . state . inputErrors
271
+ errors : this . component . state . errors ,
272
+ isValidatorUpdate : true
212
273
} ) ;
213
274
214
- if ( Object . keys ( this . component . state . inputErrors ) [ 0 ] &&
215
- form . querySelector ( `[name="${ Object . keys ( this . component . state . inputErrors ) [ 0 ] } "]` ) ) {
216
- form . querySelector ( `[name="${ Object . keys ( this . component . state . inputErrors ) [ 0 ] } "]` ) . focus ( ) ;
275
+ if ( Object . keys ( this . component . state . errors ) [ 0 ] &&
276
+ form . querySelector ( `[name="${ Object . keys ( this . component . state . errors ) [ 0 ] } "]` ) ) {
277
+ form . querySelector ( `[name="${ Object . keys ( this . component . state . errors ) [ 0 ] } "]` ) . focus ( ) ;
217
278
}
218
279
219
- return Object . keys ( this . component . state . inputErrors ) . length === 0 ;
280
+ return Object . keys ( this . component . state . errors ) . length === 0 ;
220
281
}
221
282
222
283
/**
@@ -272,7 +333,7 @@ class ReactFormValidator {
272
333
*
273
334
* @param element HTMLInputElement
274
335
*/
275
- private validate ( element : HTMLInputElement ) : object {
336
+ private validate ( element : HTMLInputElement ) : IDynamicKeyValues {
276
337
const errors = { } ;
277
338
const name = element . getAttribute ( "name" ) ;
278
339
const data = {
@@ -294,22 +355,37 @@ class ReactFormValidator {
294
355
} ) ;
295
356
}
296
357
358
+ if ( element . hasAttribute ( "data-async" ) ) {
359
+ const passes : Function = ( ) => {
360
+ delete this . errors [ name ] ;
361
+ } ;
362
+
363
+ const fails : Function = ( ) => {
364
+ const errMessage : string = validate . errors . first ( name ) ;
365
+ errors [ name ] = errMessage ;
366
+ } ;
367
+
368
+ validate . checkAsync ( passes , fails ) ;
369
+ return errors ;
370
+ }
371
+
297
372
if ( validate . fails ( ) ) {
298
373
const errMessage : string = validate . errors . first ( name ) ;
299
374
errors [ name ] = errMessage ;
300
375
return errors ;
301
376
}
377
+
302
378
delete this . errors [ name ] ;
303
379
return errors ;
304
380
}
305
381
306
382
/**
307
- * Format the error message from validatorjs error to {@link IInputErrors }
383
+ * Format the error message from validatorjs error to {@link IValidatorErrors }
308
384
*
309
385
* @param inputErrors Array of error strings
310
386
* @param errors Errors
311
387
*/
312
- private getErrorMessage ( inputErrors : Array < any > , errors : any ) : IInputErrors {
388
+ private getErrorMessage ( inputErrors : Array < any > , errors : IValidatorErrors ) : IValidatorErrors {
313
389
Object . keys ( inputErrors ) . forEach ( ( field ) => {
314
390
const msg = inputErrors [ field ] ;
315
391
if ( msg ) {
@@ -321,6 +397,17 @@ class ReactFormValidator {
321
397
} ) ;
322
398
return errors ;
323
399
}
400
+
401
+ /**
402
+ * Creating custom event to send form data.
403
+ *
404
+ * @param details The form fields to send in the event
405
+ */
406
+ private getEvent ( details : any ) : CustomEvent {
407
+ return new CustomEvent ( "onreactformsubmit" , {
408
+ detail : details
409
+ } ) ;
410
+ }
324
411
}
325
412
326
413
export default ReactFormValidator ;
0 commit comments