Skip to content

Commit 83f8575

Browse files
committed
WIP
1 parent a87b7d8 commit 83f8575

1 file changed

Lines changed: 233 additions & 15 deletions

File tree

README.md

Lines changed: 233 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,53 +7,271 @@ It is third party / framework agnostic, so you can easily add it in your stack,
77
- Heavily based on JavaScript (no html attributes annotations).
88
- Full async, all validations are processed as async using native [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) (LcFormValidation already gives you a polyfill for browsers that do not support promises).
99

10-
# Why another form library? #
10+
## Quick start
1111

12-
Form validation is a complex issue, usually we can find solutions that cover the simple scenarios and are focused on building RAD development just by adding some attributes / annotations to given fields or HTML fields (e.g. input), the disadvantage that we have found to this approach:
12+
### Defining validation constraints
13+
```js
14+
import { createFormValidation, Validators } from 'lc-form-validation';
15+
16+
const customerFormValidationConstraints = {
17+
fields: {
18+
firstName: [
19+
// Mandatory field
20+
{ validator: Validators.required },
21+
],
22+
lastName: [
23+
// Mandatory field
24+
{ validator: Validators.required }
25+
]
26+
}
27+
};
28+
```
29+
30+
Create a form validation instance
31+
32+
```js
33+
const customerFormValidation = createFormValidation(customerFormValidationConstraints);
34+
```
35+
36+
### Validating the entire form
37+
38+
```js
39+
const viewModel = { login: 'jdoe@example.com', password: 'jdoe3981' };
40+
customerFormValidation
41+
.validateForm(viewModel)
42+
.then((validationResult) => {
43+
console.log(validationResult.success); // true
44+
console.log(validationResult.formGlobalErrors); // []
45+
console.log(validationResult.fieldErrors);
46+
/*[
47+
{ succeeded: true, type: "REQUIRED", key: "firstName", errorMessage: "" },
48+
{ succeeded: true, type: "REQUIRED", key: "lastName", errorMessage: "" }
49+
]*/
50+
})
51+
.catch(error => {
52+
// handle unexpected errors
53+
});
54+
```
55+
56+
### Validate a single field
57+
58+
```js
59+
60+
// Successful validation
61+
customerFormValidation
62+
.validateField(null, 'firstName', 'John')
63+
.then((validationResult) => {
64+
console.log(validationResult.succeeded); // true
65+
console.log(validationResult.type); // "REQUIRED"
66+
console.log(validationResult.key); // "firstName"
67+
console.log(validationResult.errorMessage); // ""
68+
})
69+
.catch(error => {
70+
// handle unexpected errors
71+
});
72+
73+
// Unsuccessful validation
74+
customerFormValidation
75+
.validateField(null, 'lastName', '')
76+
.then((validationResult) => {
77+
console.log(validationResult.succeeded); // false
78+
console.log(validationResult.type); // "REQUIRED"
79+
console.log(validationResult.key); // "lastName"
80+
console.log(validationResult.errorMessage);
81+
// "Please, fill in this mandatory field."
82+
})
83+
.catch(error => {
84+
// handle unexpected errors
85+
});
86+
```
87+
88+
### Trigger certain validations in each constraint:
89+
90+
Add `eventsFilter` with on or more events:
91+
```js
92+
import {
93+
createFormValidation,
94+
Validators,
95+
FieldValidationResult
96+
} from 'lc-form-validation';
97+
98+
const loginFormValidationConstraints = {
99+
fields: {
100+
login: [
101+
// Mandatory field
102+
{ validator: Validators.required },
103+
// It has to be a valid email address
104+
{
105+
validator: Validators.email
106+
eventsFilter: { onBlur: true }, // <-- apply a filter
107+
}
108+
]
109+
}
110+
};
111+
const loginFormValidation = createFormValidation(loginValidationConstraints);
112+
```
113+
114+
Trigger field validation:
115+
```js
116+
loginFormValidation
117+
.validateField(null, 'login', 'jdoe', { onBlur: true})
118+
.then((validationResult) => {
119+
console.log(validationResult.succeeded); // false
120+
console.log(validationResult.type); // "EMAIL"
121+
console.log(validationResult.key); // "login"
122+
console.log(validationResult.errorMessage);
123+
// 'Please enter a valid email address.'
124+
})
125+
.catch(error => {
126+
// handle unexpected errors
127+
});
128+
```
129+
> Not passing `eventsFilter` as third parameter results in `{ onChange: true }` by default.
130+
131+
### Passing extra arguments
132+
133+
You can pass custom parameters that will be injected into the validator using the `customParams` property:
134+
135+
```js
136+
const loginFormValidationConstraints = {
137+
fields: {
138+
login: [
139+
// Mandatory field
140+
{ validator: Validators.required },
141+
// It has to be a valid email address
142+
{ validator: Validators.email },
143+
{
144+
validator: Validators.pattern,
145+
customParams: {
146+
// It must belong to "lemoncode.net" domain
147+
pattern: /\@lemoncode.net$/
148+
}
149+
}
150+
]
151+
}
152+
};
153+
```
154+
155+
### Custom validators:
156+
157+
#### Field validator
158+
159+
A field validator must return a `FieldValidationResult`. You can pass the entire `viewModel` if you need to validate a field based on other fields:
13160

14-
* There are different approaches for each scenario: sometimes you have to add some tweaking for async validations, some other take care of special scenarios (like validations that depends on more than one field).
161+
```js
162+
function allowLowerCaseOnly(value, viewModel, customParams) {
163+
const isValid = value.toLowerCase() === value;
164+
const errorMessage = 'Field must be lowercase.`;
165+
const validationResult = new FieldValidationResult();
166+
validationResult.succeeded = isValid;
167+
validationResult.errorMessage = isValid ? '' : errorMessage;
168+
validationResult.type = 'LOWER_CASE';
15169
16-
* Usually you can easily unit test one validation (directive / annotation), but testing the whole form is a complex task (directives are coupled to e.g. HTML).
170+
return validationResult;
171+
}
172+
```
17173
18-
* Validations are tightly coupled to e.g. directives or markup is not easy to reuse this validation code in e.g. server side (universal javascript).
174+
Apply inside your constraints:
19175
20-
## Overview
176+
```js
177+
const signupValidationConstraints = {
178+
fields: {
179+
username: [
180+
{ validator: allowLowerCaseOnly }
181+
]
182+
}
183+
};
184+
```
185+
186+
#### Global form validator
187+
188+
A global validator will accept the entire viewModel and return a `FieldValidationResult`. Put your global validators inside `global` property of your validation constraints. It is useful, e.g., for validating dynamic properties:
189+
190+
```js
191+
function validateQuestions(...questions) {
192+
// All questions must be answered.
193+
return questions.every((question) => question.answer.trim().length);
194+
}
195+
196+
function questionsValidator(viewModel) {
197+
const isValid = validateQuestions(viewModel.questions);
198+
const errorMessage = 'You must answer all questions.';
199+
const validationResult = new FieldValidationResult();
200+
201+
validationResult.succeeded = isValid;
202+
validationResult.errorMessage = isValid ? '' : errorMessage;
203+
validationResult.type = '';
204+
205+
return validationResult;
206+
}
207+
208+
const testFormValidationConstraints = {
209+
global: [questionsValidator]
210+
};
211+
212+
const testFormValidation = createFormValidation(testFormValidationConstraints);
213+
214+
const viewModel = {
215+
questions: [
216+
{ id: 29, title: 'What method does merge two or more objects?', anwser: 'Object.assign' },
217+
{ id: 14, title: '', anwser: '' },
218+
{ id: 42, title: '', anwser: '' },
219+
{ id: 85, title: '', anwser: '' },
220+
]
221+
};
222+
testFormValidation.validateForm()
223+
```
224+
225+
## Documentation
226+
227+
You can see the full documentation in the [github.io page](http://lemoncode.github.io/lcFormValidation/).
21228
22229
## Examples
23230
24231
### React examples
25232
26-
#### Simple form
233+
#### Simple form ([ES6](), [TypeScript](./samples/react/00%20SimpleForm))
27234
28235
A simple form with fullname and password fields. Applied validations:
29236
30237
- Both fullname and password fields are mandatory (required validator + custom validator).
31-
[View source code](./samples/react/00%20SimpleForm)
32238
33-
#### Signup form
239+
#### Signup form ([ES6](), [TypeScript](./samples/react/01%20SignupForm))
34240
35241
A sign up form with username, password and confirm password fields with the next validation constraints:
36242
37243
- username is mandatory and has to not exist on GitHub (required validator + custom validator).
38244
- password is mandatory and has to be minimum 4 characters length (minLength validator).
39245
- confirm password is mandatory and has to be the same value as password field (custom validator).
40-
[View source code](./samples/react/01%20SignupForm)
41246
42-
#### Quiz form
247+
#### Quiz form ([ES6](), [TypeScript](./samples/react/02%20QuizForm))
43248
44249
A simple quiz with three options where there has to be at least one checked option to pass the validation (custom global validation).
45-
[View source code](./samples/react/02%20QuizForm)
46-
47250
48251
### jQuery examples
49252
50-
#### Shopping form
253+
#### Shopping form ([ES6](./samples/jquery/Shopping%20form))
51254
52255
A little shopping form where the user has to select a product with a version and optionally apply a discount code and enter its NIF. Validations applied:
53256
54257
- The brand and product fields are mandatory (required validator).
55258
- The discount code is optional but needs to have a valid format if fulfilled (pattern validator).
56259
- NIF field is mandatory with a valid format (required validator + custom validator
57-
[View source code](./samples/jquery/Shopping%20form)
260+
261+
## Why another form library?
262+
263+
Form validation is a complex issue, usually we can find solutions that cover the simple scenarios and are focused on building RAD development just by adding some attributes / annotations to given fields or HTML fields (e.g. input), the disadvantage that we have found to this approach:
264+
265+
- There are different approaches for each scenario: sometimes you have to add some tweaking for async validations, some other take care of special scenarios (like validations that depends on more than one field).
266+
267+
- Usually you can easily unit test one validation (directive / annotation), but testing the whole form is a complex task (directives are coupled to e.g. HTML).
268+
269+
- Validations are tightly coupled to e.g. directives or markup is not easy to reuse this validation code in e.g. server side (universal javascript).
58270
59271
We are looking for contributors to implement samples and support for libraries such as Angularjs, Ember... if you would like to cooperate don't hesitate contacting us.
272+
273+
## License
274+
[MIT](./LICENSE)
275+
276+
# About Lemoncode
277+
We are a team of long-term experienced freelance developers, established as a group in 2010. We specialize in Front End technologies and .NET. [Click here](http://lemoncode.net/services/en/#en-home) to get more info about us.

0 commit comments

Comments
 (0)