Skip to content

Commit 89f25fe

Browse files
committed
[WIP] Add more apis and refactor some existing apis.
Signed-off-by: gokulakannant <gokulakannanthangaraji@gmail.com>
1 parent e2772f8 commit 89f25fe

38 files changed

+2460
-162
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class ValidationForm extends React.Component {
3434
email: "",
3535
phone_number: ""
3636
},
37-
inputErrors: {}
37+
errors: {}
3838
};
3939
this.form = new ReactFormValidation(
4040
this,
@@ -64,7 +64,7 @@ class ValidationForm extends React.Component {
6464
/>
6565
</label>
6666
<label className="error">
67-
{this.state.inputErrors.name ? this.state.inputErrors.name.message : ""}
67+
{this.state.errors.name ? this.state.errors.name.message : ""}
6868
</label>
6969
</p>
7070

@@ -80,7 +80,7 @@ class ValidationForm extends React.Component {
8080
/>
8181
</label>
8282
<label className="error">
83-
{this.state.inputErrors.email ? this.state.inputErrors.email.message : ""}
83+
{this.state.errors.email ? this.state.errors.email.message : ""}
8484
</label>
8585
</p>
8686

@@ -96,7 +96,7 @@ class ValidationForm extends React.Component {
9696
/>
9797
</label>
9898
<label className="error">
99-
{this.state.inputErrors.phone_number ? this.state.inputErrors.phone_number.message : ""}
99+
{this.state.errors.phone_number ? this.state.errors.phone_number.message : ""}
100100
</label>
101101
</p>
102102
<p>

src/index.ts

Lines changed: 139 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
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";
33

4-
class ReactFormValidator {
4+
class ReactFormValidator extends EventTarget {
55
private component: IReactComponent;
6-
private submitCallback: Function;
76
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;
927

1028
/**
1129
* Construct the React Input Form Validator instance.
@@ -17,23 +35,18 @@ class ReactFormValidator {
1735
* @param options {@link Options}
1836
* @example
1937
* ```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,
2741
* {
2842
* locale: 'en'
2943
* })
3044
* ```
3145
*/
32-
constructor(component: IReactComponent, rules: object, callback: Function, options?: IOptions) {
46+
constructor(component: IReactComponent, options?: IOptions) {
47+
super();
3348
ReactFormValidator.useLang((options && options.locale) ? options.locale : "en");
3449
this.component = component;
35-
this.rules = rules;
36-
this.submitCallback = callback;
3750
this.handleFieldsChange = this.handleFieldsChange.bind(this);
3851
this.handleSubmit = this.handleSubmit.bind(this);
3952
this.handleBlurEvent = this.handleBlurEvent.bind(this);
@@ -45,9 +58,8 @@ class ReactFormValidator {
4558
* @param locale string
4659
* @example
4760
* ```js
48-
* import ReactInputFormValidator from "react-input-form-validator";
4961
*
50-
* ReactInputFormValidator.useLang("en");
62+
* ReactFormValidator.useLang("en");
5163
* ```
5264
*/
5365
static useLang(locale: string): void {
@@ -63,7 +75,8 @@ class ReactFormValidator {
6375
* :attribute inside errorMessage will be replaced with the attribute name.
6476
* @example
6577
* ```js
66-
* ReactInputFormValidator.register('telephone', function(value, requirement, attribute) {
78+
*
79+
* ReactFormValidator.register('telephone', function(value, requirement, attribute) {
6780
* return value.match(/^\d{3}-\d{3}-\d{4}$/);
6881
* }, 'The :attribute phone number is not in the format XXX-XXX-XXXX.');
6982
*
@@ -73,6 +86,26 @@ class ReactFormValidator {
7386
Validator.register(name, callbackFn, errorMessage);
7487
}
7588

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+
76109
/**
77110
* You can also add your own custom language by calling setMessages:
78111
*
@@ -81,7 +114,7 @@ class ReactFormValidator {
81114
* @example
82115
* ```js
83116
*
84-
* ReactInputFormValidator.setMessages('lang_code', {
117+
* ReactFormValidator.setMessages('lang_code', {
85118
* required: 'The :attribute field is required.'
86119
* });
87120
* ```
@@ -95,7 +128,8 @@ class ReactFormValidator {
95128
* @param name The name of the rule
96129
* @example
97130
* ```js
98-
* ReactInputFormValidator.getMessages('lang_code');
131+
*
132+
* ReactFormValidator.getMessages('lang_code');
99133
* ```
100134
*/
101135
static getMessages(name: string): object {
@@ -106,31 +140,65 @@ class ReactFormValidator {
106140
* Get the default language being used
107141
* @example
108142
* ```js
109-
* ReactInputFormValidator.getDefaultLang(); // returns e.g. 'en'
143+
*
144+
* ReactFormValidator.getDefaultLang(); // returns e.g. 'en'
110145
* ```
111146
*/
112147
static getDefaultLang(): string {
113148
return Validator.getDefaultLang();
114149
}
115150

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+
116183
/**
117184
* Handle onchange event for input fields.
118185
*
119186
* @example
120187
* ```js
188+
*
121189
* // Refer "ReactFormValidator Interface" for react input form validator object creation
122190
*
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}>
124192
* ```
125193
*/
126194
public handleFieldsChange(event) {
127-
const name = event.target.name;
195+
const name: string = event.target.name;
128196
if (this.component && name) {
129197
const fields = Object.assign({}, this.component.state.fields);
130198
fields[name] = (event.target.type === "checkbox") ? this.getCheckboxValues(event.target) :
131199
(event.target.type === "radio") ? this.getRadioButtonValues(event.target) :
132200
event.target.value;
133-
this.component.setState({ fields: fields, isOwnUpdate: true });
201+
this.component.setState({ fields: fields, isValidatorUpdate: true });
134202
}
135203
}
136204

@@ -140,6 +208,7 @@ class ReactFormValidator {
140208
* @param event onsubmit event
141209
* @example
142210
* ```js
211+
*
143212
* // Refer "ReactFormValidator Interface" for react input form validator object creation
144213
*
145214
* <form onSubmit={form.handleSubmit}>
@@ -148,8 +217,8 @@ class ReactFormValidator {
148217
*/
149218
public handleSubmit(event) {
150219
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));
153222
}
154223
}
155224

@@ -159,6 +228,7 @@ class ReactFormValidator {
159228
* @param event onblur event
160229
* @example
161230
* ```js
231+
*
162232
* // Refer "ReactFormValidator Interface" for react input form validator object creation
163233
* <input
164234
* name="email"
@@ -170,21 +240,11 @@ class ReactFormValidator {
170240
*/
171241
public handleBlurEvent(event) {
172242
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 });
188248
}
189249
}
190250

@@ -196,27 +256,28 @@ class ReactFormValidator {
196256
private validateForm(form): boolean {
197257
if (!this.component || !this.component.state) {
198258
this.component.state = {
199-
inputErrors: {}
259+
errors: {}
200260
};
201261
}
202262

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])")
204264
.forEach((elem) => {
205265
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));
208268
});
209269

210270
this.component.setState({
211-
inputErrors: this.component.state.inputErrors
271+
errors: this.component.state.errors,
272+
isValidatorUpdate: true
212273
});
213274

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();
217278
}
218279

219-
return Object.keys(this.component.state.inputErrors).length === 0;
280+
return Object.keys(this.component.state.errors).length === 0;
220281
}
221282

222283
/**
@@ -272,7 +333,7 @@ class ReactFormValidator {
272333
*
273334
* @param element HTMLInputElement
274335
*/
275-
private validate(element: HTMLInputElement): object {
336+
private validate(element: HTMLInputElement): IDynamicKeyValues {
276337
const errors = {};
277338
const name = element.getAttribute("name");
278339
const data = {
@@ -294,22 +355,37 @@ class ReactFormValidator {
294355
});
295356
}
296357

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+
297372
if (validate.fails()) {
298373
const errMessage: string = validate.errors.first(name);
299374
errors[name] = errMessage;
300375
return errors;
301376
}
377+
302378
delete this.errors[name];
303379
return errors;
304380
}
305381

306382
/**
307-
* Format the error message from validatorjs error to {@link IInputErrors}
383+
* Format the error message from validatorjs error to {@link IValidatorErrors}
308384
*
309385
* @param inputErrors Array of error strings
310386
* @param errors Errors
311387
*/
312-
private getErrorMessage(inputErrors: Array<any>, errors: any): IInputErrors {
388+
private getErrorMessage(inputErrors: Array<any>, errors: IValidatorErrors): IValidatorErrors {
313389
Object.keys(inputErrors).forEach((field) => {
314390
const msg = inputErrors[field];
315391
if (msg) {
@@ -321,6 +397,17 @@ class ReactFormValidator {
321397
});
322398
return errors;
323399
}
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+
}
324411
}
325412

326413
export default ReactFormValidator;

0 commit comments

Comments
 (0)