@dolanske/v-valid 2.1.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,521 +1,470 @@
1
- # Validate forms
2
-
3
- You know what they say, you can not trust a thief or a murderer. You know who you can trust even less? That's right, the u s e r 😤. Therefore we need to strictly watch their every step and every action. Especially when we ask THEM (dear god) to input something in a form and send it to us (YIKES).
4
-
5
- ## Example + Setup
6
-
7
- 1. Install the package via npm
8
-
9
- ```cmd
10
- npm i @dolanske/v-valid
11
- ```
12
-
13
- 2. Set up your component
14
-
15
- ```ts
16
- import {
17
- defineRules,
18
- minLength,
19
- required,
20
- type,
21
- useValidation,
22
- validateIf,
23
- } from '@dolanske/validate'
24
- import { computed, reactive } from 'vue'
25
-
26
- // Create your form
27
- const form = reactive({
28
- value: [1, 2, 3],
29
- info: {
30
- contact: 'the_value_should_be_an_email'
31
- }
32
- })
33
-
34
- // Create rules. Use the form object as a type helper
35
- const rules = defineRules<typeof form>({
36
- // Rule keys must match the keys of your form
37
- value: {
38
- // Value is required
39
- required,
40
- // Using a helper, we can add conditional validations
41
- // Here we check if length of value is larger than 1 but ONLY if the
42
- // value is an array
43
- shouldCheckType: validateIf(
44
- () => Array.isArray(form.value),
45
- minLength(1)
46
- )
47
- },
48
- // You can also nest rules and define them using an array, if you don't
49
- // need custom name.
50
- info: {
51
- contact: [required, email, contains(['@', '.com'])]
52
- }
53
- })
54
-
55
- // Create your validation instance
56
- const {
57
- validate,
58
- errors,
59
- reset,
60
- } = useValidation(form, rules)
61
- ```
62
-
63
- 3. Trigger form validation whenever and wherever you want
64
-
65
- ```ts
66
- async function onSubmit() {
67
- validate()
68
- // The ctx variable is the same for both failed and passed validation
69
- .then((ctx) => {
70
- /* Validation passed */
71
- })
72
- .catch((ctx) => {
73
- /* Validation failed */
74
-
75
- // Extract a rule property for its information
76
- const { info } = ctx
77
- info.contact.id // 'contact'
78
- info.contact.value // whatever user inputted
79
- info.contact.invalid // 'true'
80
- info.contact.errors.email // 'Value must be in a valid email format'
81
- })
82
- }
83
- ```
84
-
85
- ## API
86
-
87
- ### `useValidation(form, rules, options)`
88
-
89
- Main composable which is used to initiate form validation context. It accepts 2 required and 1 optional param
90
-
91
- - `form` reactive form object
92
- - `rules` object containing the ruleset
93
- - `options` (optional)
94
- - `autoclear` (default: false) resets all errors the first time a form is updated after `validate()` was ran
95
- - `proactive` (default: false) runs form validation on every input
96
-
97
- ### Composable returns
98
-
99
- ##### `validate(...ruleIdsToValidateOnly: string[])`
100
-
101
- Performs the form validation and resolves with a `Promise<Context>`. The same context object as descibed in point 3. in the example
102
-
103
- Optionally, you can input an array of keys, which are present in the form object. Only those keys will then be validated and the rest of the form will be ignored.
104
-
105
- ##### `reset()`
106
-
107
- Will reset the current state of the validation state.
108
-
109
- ##### `addError(path: string, error: { key: string, message: string })`
110
-
111
- Appends a new error to the error object at the provided path. This is meant for complex usage or for library/plugin authors
112
-
113
- ```ts
114
- addError('info.contact', {
115
- key: 'required',
116
- message: 'In fact, the contact info is required.'
117
- })
118
- ```
119
-
120
- #### Validation state
121
-
122
- - `errors: Ref<Context>` - Validation form state, might not contain much before the first validation has been ran
123
- - `anyError: Ref<boolean>` - Wether any error at all was found
124
- - `pending: Ref<boolean>` - Wether the form is currently being validated. This can be useful if you have one or more async rules
125
-
126
- ---
127
-
128
- ## Validators
129
-
130
- Collection of validators used to construct rule objects
131
-
132
- ### `required`
133
-
134
- Requires non-empty value.
135
-
136
- ```js
137
- const rules = {
138
- name: { required }
139
- }
140
- ```
141
-
142
- ---
143
-
144
- ### `minLength(min)`
145
-
146
- Requires input to have a minimal specified length.
147
-
148
- - **Parameters**
149
- - `Ref<number> | number` min
150
- - **Works with**
151
- - `string`, `Set<any>`, `Map<any, any>`, `any[]` and `Object`
152
-
153
- ```js
154
- const rules = {
155
- name: { minLength: minLength(10) }
156
- }
157
- ```
158
-
159
- ---
160
-
161
- ### `maxLength(max)`
162
-
163
- Requires input to have a maximum specified length.
164
-
165
- - **Parameters**
166
- - `Ref<number> | number` max
167
- - **Works with**
168
- - `string`, `Set<any>`, `Map<any, any>`, `any[]`, `Object`
169
-
170
- ```js
171
- const rules = {
172
- name: { minLength: minLength(10) }
173
- }
174
- ```
175
-
176
- ---
177
-
178
- ### `minLenNoSpace`
179
-
180
- Input must be a string and excluding spaces must be equal or greater to the provided min value.
181
-
182
- - **Parameters**
183
- - `Ref<number> | number` min - Minimum allowed string length
184
- - **Works with**
185
- - `string`
186
-
187
- ```js
188
- const rules = {
189
- value: {
190
- // A B C D E F => false
191
- // A B C D E F G => true
192
- min: minLenNoSpace(7)
193
- }
194
- }
195
- ```
196
-
197
- ---
198
-
199
- ### `maxLenNoSpace`
200
-
201
- Input must be a string and excluding spaces must be equal or lesser to the provided max value.
202
-
203
- - **Parameters**
204
- - `Ref<number> | number` max - Maximum allowed string length
205
- - **Works with**
206
- - `string`
207
-
208
- ```js
209
- const rules = {
210
- value: {
211
- // H e l l 0 => true
212
- // Bummer => false
213
- max: maxLenNoSpace(5)
214
- }
215
- }
216
- ```
217
-
218
- ---
219
-
220
- ### `between(min, max)`
221
-
222
- Requires input to be a number or Date within the specified bounds.
223
-
224
- - **Parameters**
225
- - `Ref<number> | number | Date` min
226
- - `Ref<number> | number | Date` max
227
- - **Works with**
228
- - `number`, `Date`
229
-
230
- ```js
231
- const rules = {
232
- numberVal: { between: between(0, 10) },
233
- dateVal: {
234
- between: between(new Date('12/24/2020'), new Date('12/24/2022').getTime())
235
- }
236
- }
237
- ```
238
-
239
- ---
240
-
241
- ### `url`
242
-
243
- Requires input to be a valid URL.
244
-
245
- ---
246
-
247
- ### `email`
248
-
249
- Requires input to be a valid email address.
250
-
251
- ---
252
-
253
- ### `decimal`
254
-
255
- Requires input to be a number and contain decimals.
256
-
257
- ---
258
-
259
- ### `hasSpecialChars`
260
-
261
- Checks wether an input contains any special characters
262
-
263
- ---
264
-
265
- ### `sameAs(value, lenient)`
266
-
267
- Requires input to be the same as the provided value in the rule.
268
-
269
- - **Parameters**
270
- - `Ref<any> | any` value
271
- - `boolean` (default: false) lenient - wether to only compare based on value or value and type (eg. `==` vs `===`)
272
- - **Works with**
273
- - `any`
274
-
275
- ```js
276
- const rules = {
277
- name: { sameAs: sameAs('Hello world') }
278
- }
279
- ```
280
-
281
- ---
282
-
283
- ### `match(regex)`
284
-
285
- Requires value to match the provided regular expression check.
286
-
287
- - **Parameters**
288
- - `string | RegExp` regex
289
- - **Works with**
290
- - `string`
291
-
292
- ```js
293
- // Checks wether string contains only letters and numbers
294
- const rules = {
295
- name: {
296
- noSpecialChars: match(/[^a-z0-9]/i)
297
- }
298
- }
299
- ```
300
-
301
- ---
302
-
303
- ### `contains(items, split)`
304
-
305
- Checks wether string input contains certain words or characters. If you write multiple words. It will check against each word individually.
306
-
307
- - **Parameters**
308
- - `string | string[]` items - Words to check for
309
- - `boolean` (default: false) exact - Wether to match against the entire input string or split it by spaces. Default to true
310
- - **Works With**
311
- - `string`
312
-
313
- ```js
314
- const rules = {
315
- value: {
316
- // Input: 'Hello Dear World' => false
317
- containsExact: contains('Hello World', true),
318
- // Input: 'Hello World' => true
319
- containsSome: contains('Hello Dear World')
320
- }
321
- }
322
- ```
323
-
324
- ---
325
-
326
- ### `startsWith(str, pos)` & `endsWith(str, pos)`
327
-
328
- Checks wether the input value is a string and starts or ends with the provided parameter.
329
- You can optionally specify the position where the check begins in the input value
330
-
331
- - **Parameters**
332
- - `string | Ref<string>` String we are matching against
333
- - `number` Starting position of the matching
334
- - *Note*: When using `endsWith`, the position starts at the end and goes backwards
335
- - **Works with**
336
- - `string`
337
-
338
- ```js
339
- const rules = {
340
- value: {
341
- // Input 'Hello World' => true
342
- start: startsWith('llo', 2),
343
- // Input 'Hello World' => false
344
- endsBad: endsWith('Hello')
345
- }
346
- }
347
- ```
348
-
349
- ---
350
-
351
- ### `type[typeToCheckFor]`
352
-
353
- This is a special object rule in which you need to specify the type you want to check against.
354
- Note, each type check is also available as a standalone import without using the `type` object. Only difference is
355
- that you need to prefix it.
356
-
357
- ```js
358
- // This import method
359
- import { isMap, isStr } from 'v-valid'
360
- // Equals to this one
361
- import { type } from 'v-valid'
362
- const { str, map } = type
363
- ```
364
-
365
- - **Available type checks**
366
- - `type.str` - requires value to be an instance of `string`
367
- - `type.num` - requires value to be an instance of `number`
368
- - `type.arr` - requires value to be an instance of `Array`
369
- - `type.obj` - requires value to be an instance of `Object`
370
- - `type.set` - requires value to be an instance of `Set`
371
- - `type.map` - requires value to be an instance of `Map`
372
- - `type.date` - requires value to be an instance of `Date`
373
- - `type.symbol` - requires value to be an instance of `Symbol`
374
-
375
- ```js
376
- const rules = {
377
- name: { array: type.arr }
378
- }
379
- ```
380
-
381
- ## Helpers
382
-
383
- ### `withLabel(label, rule)`
384
-
385
- Allows you to add a custom error message to ant validation rule.
386
-
387
- - **Parameters**
388
- - `string | Label` label
389
- - `ValidationRule | ValidationRuleObject` rule
390
-
391
- ```js
392
- const rules = {
393
- name: {
394
- required: withLabel('You MUST fill this input!!', required)
395
- }
396
- }
397
- ```
398
-
399
- We can also use a method instead of a string when creating custom label. This method receives the validated `value` as the first parameter and all the subsequent parameters from the validation rule. If there are any.
400
-
401
- ```js
402
- const rules = {
403
- name: {
404
- between: withLabel(
405
- (value, min, max) =>
406
- `The value (${value}) must fit between ${min} and ${max}`,
407
- // If error occurs, the message will read like this:
408
- // 'The value (<value>) must fit between 5 and 10'
409
- between(5, 10)
410
- )
411
- }
412
- }
413
- ```
414
-
415
- ---
416
-
417
- ### `and(rules)`, `or(rules)`
418
-
419
- Used when we want to apply multiple rules together into one.
420
-
421
- - `and(...ValidationRule[])` Requires all rules to be passing to resolve
422
- - `or(...ValidationRule[])` Requires at least one rule to be passing to resolve
423
-
424
- ```js
425
- const rules = {
426
- name: {
427
- // We want the value to be an array with at least 10 items in it
428
- group: and(type.arr, minLength(10))
429
- }
430
- }
431
- ```
432
-
433
- ### `not(rules)`
434
-
435
- Used in situations where we want to invert a rule results. Can also be used with multiple rules, only resolving when all of them are failing.
436
-
437
- ```js
438
- const rules = {
439
- name: {
440
- // We want the input value to be anything but a string
441
- notString: not(type.str)
442
- }
443
- }
444
- ```
445
-
446
- ---
447
-
448
- ### `validateIf(condition, rule)`, `validateIfNot(condition, rule)`
449
-
450
- Performs validation if the provided condition is met. Due to implementation limits, you can't use a rule as a condition. For that please use the `and` helper.
451
-
452
- - **Parameters**
453
- - `boolean | () => boolean | Ref<boolean> | Promise<boolean>` condition
454
- - `ValidationRule` rule
455
-
456
- ```js
457
- const rules = {
458
- name: {
459
- // We want to check min length of value but only if value is an array
460
- minLength: validateIf(Array.isArray(form.value), minLength(5)),
461
-
462
- // If value is not a number, required it to match "Hello World"
463
- sameAs: validateIfNot(typeof form.value === 'number', sameAs('Hello World'))
464
- }
465
- }
466
- ```
467
-
468
- ---
469
-
470
- ### `createRule(rule, message): ValidationRule`
471
-
472
- Helpers used to create validation rules
473
-
474
- - **Parameters**
475
- - `(value: any, args: Record<string, unknown>) => boolean | Promise<boolean>` rule
476
- - `string | Label | undefined` label
477
-
478
- ```js
479
- // Define rule without any parameters
480
- const required = createRule(
481
- value => value !== undefined && value !== null,
482
- 'Value is required'
483
- )
484
-
485
- // Defining rules with parameters. Returns a function which must be called when
486
- // rule is being used
487
- const arrAndMinLen = createRuleArg<{ length: number }> (
488
- (value, { length }) => isArray(value) && value.length >= length,
489
- (_, { length }) => `Array with at least ${length} length`
490
- )
491
-
492
- // Asynchronous rule, the main use case here is validating
493
- // login passwords or other API related things
494
- const checkPassword = createRule(
495
- (password) =>
496
- new Promise((resolve) => {
497
- const result = await someApiCall(password)
498
- resolve(result)
499
- }),
500
- 'Incorrect password. Please try again.'
501
- )
502
- ```
503
-
504
- ---
505
-
506
- ### `$test(rule, value): Promise<boolean> | boolean`
507
-
508
- Perform a synthetic validation outside of any validation scope.
509
-
510
- - **Parameters**
511
- - `ValidationRule` rule
512
- - `value` any
513
-
514
- ```js
515
- const result = $test(minLength(5), [1, 2, 3]) // False
516
- const result = $test(maxLength(5), [1, 2, 3]) // True
517
- const result = $test(type.symbol, Symbol('Symbol')) // True
518
-
519
- // With `and`, `not`, `or` and any async custom validators we have to await the returned promise
520
- const result = await $test(or(type.str, type.num), 'Hello World') // True
521
- ```
1
+ # Validate forms
2
+
3
+ You know what they say, you can not trust a thief or a murderer. You know who you can trust even less? That's right, the u s e r 😤. Therefore we need to strictly watch their every step and every action. Especially when we ask THEM (dear god) to input something in a form and send it to us (YIKES).
4
+
5
+ ## Example + Setup
6
+
7
+ 1. Install the package via npm
8
+
9
+ ```cmd
10
+ npm i @dolanske/v-valid
11
+ ```
12
+
13
+ 2. Set up your component
14
+
15
+ ```ts
16
+ import {
17
+ defineRules,
18
+ minLength,
19
+ required,
20
+ type,
21
+ useValidation,
22
+ validateIf,
23
+ } from '@dolanske/validate'
24
+ import { computed, reactive } from 'vue'
25
+
26
+ // Create your form
27
+ const form = reactive({
28
+ value: [1, 2, 3],
29
+ info: {
30
+ contact: 'the_value_should_be_an_email'
31
+ }
32
+ })
33
+
34
+ // Create rules. Use the form object as a type helper
35
+ const rules = defineRules<typeof form>({
36
+ // Rule keys must match the keys of your form
37
+ value: {
38
+ // Value is required
39
+ required,
40
+ // Using a helper, we can add conditional validations
41
+ // Here we check if length of value is larger than 1 but ONLY if the
42
+ // value is an array
43
+ shouldCheckType: validateIf(
44
+ () => Array.isArray(form.value),
45
+ minLength(1)
46
+ )
47
+ },
48
+ // You can also nest rules and define them using an array, if you don't
49
+ // need custom name.
50
+ info: {
51
+ contact: [required, email, contains(['@', '.com'])]
52
+ }
53
+ })
54
+
55
+ // Create your validation instance
56
+ const {
57
+ validate,
58
+ errors,
59
+ reset,
60
+ } = useValidation(form, rules)
61
+ ```
62
+
63
+ 3. Trigger form validation whenever and wherever you want
64
+
65
+ ```ts
66
+ async function onSubmit() {
67
+ validate()
68
+ // The ctx variable is the same for both failed and passed validation
69
+ .then((ctx) => {
70
+ /* Validation passed */
71
+ })
72
+ .catch((ctx) => {
73
+ /* Validation failed */
74
+
75
+ // Extract a rule property for its information
76
+ const { info } = ctx
77
+ info.contact.id // 'contact'
78
+ info.contact.value // whatever user inputted
79
+ info.contact.invalid // 'true'
80
+ info.contact.errors.email // 'Value must be in a valid email format'
81
+ })
82
+ }
83
+ ```
84
+
85
+ ## API
86
+
87
+ ### `useValidation(form, rules, options)`
88
+
89
+ Main composable which is used to initiate form validation context. It accepts 2 required and 1 optional param
90
+
91
+ - `form` reactive form object
92
+ - `rules` object containing the ruleset
93
+ - `options` (optional)
94
+ - `autoclear` (default: false) resets all errors the first time a form is updated after `validate()` was ran
95
+ - `proactive` (default: false) runs form validation on every input
96
+
97
+ ### Composable returns
98
+
99
+ ##### `validate(...ruleIdsToValidateOnly: string[])`
100
+
101
+ Performs the form validation and resolves with a `Promise<Context>`. The same context object as descibed in point 3. in the example
102
+
103
+ Optionally, you can input an array of keys, which are present in the form object. Only those keys will then be validated and the rest of the form will be ignored.
104
+
105
+ ##### `reset()`
106
+
107
+ Will reset the current state of the validation state.
108
+
109
+ ##### `addError(path: string, error: { key: string, message: string })`
110
+
111
+ Appends a new error to the error object at the provided path. This is meant for complex usage or for library/plugin authors
112
+
113
+ ```ts
114
+ addError('info.contact', {
115
+ key: 'required',
116
+ message: 'In fact, the contact info is required.'
117
+ })
118
+ ```
119
+
120
+ #### Validation state
121
+
122
+ - `errors: Ref<Context>` - Validation form state, might not contain much before the first validation has been ran
123
+ - `anyError: Ref<boolean>` - Wether any error at all was found
124
+ - `pending: Ref<boolean>` - Wether the form is currently being validated. This can be useful if you have one or more async rules
125
+
126
+ ---
127
+
128
+ ## Validators
129
+
130
+ Collection of validators used to construct rule objects
131
+
132
+ ### `required`
133
+
134
+ Requires non-empty value.
135
+
136
+ ```js
137
+ const rules = {
138
+ name: { required }
139
+ }
140
+ ```
141
+
142
+ ---
143
+
144
+ ### `minLength(min)`
145
+
146
+ Requires input to have a minimal specified length.
147
+
148
+ - **Parameters**
149
+ - `Ref<number> | number` min
150
+ - **Works with**
151
+ - `string`, `Set<any>`, `Map<any, any>`, `any[]` and `Object`
152
+
153
+ ```js
154
+ const rules = {
155
+ name: { minLength: minLength(10) }
156
+ }
157
+ ```
158
+
159
+ ---
160
+
161
+ ### `maxLength(max)`
162
+
163
+ Requires input to have a maximum specified length.
164
+
165
+ - **Parameters**
166
+ - `Ref<number> | number` max
167
+ - **Works with**
168
+ - `string`, `Set<any>`, `Map<any, any>`, `any[]`, `Object`
169
+
170
+ ```js
171
+ const rules = {
172
+ name: { minLength: minLength(10) }
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ### `minLenNoSpace`
179
+
180
+ Input must be a string and excluding spaces must be equal or greater to the provided min value.
181
+
182
+ - **Parameters**
183
+ - `Ref<number> | number` min - Minimum allowed string length
184
+ - **Works with**
185
+ - `string`
186
+
187
+ ```js
188
+ const rules = {
189
+ value: {
190
+ // A B C D E F => false
191
+ // A B C D E F G => true
192
+ min: minLenNoSpace(7)
193
+ }
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ### `maxLenNoSpace`
200
+
201
+ Input must be a string and excluding spaces must be equal or lesser to the provided max value.
202
+
203
+ - **Parameters**
204
+ - `Ref<number> | number` max - Maximum allowed string length
205
+ - **Works with**
206
+ - `string`
207
+
208
+ ```js
209
+ const rules = {
210
+ value: {
211
+ // H e l l 0 => true
212
+ // Bummer => false
213
+ max: maxLenNoSpace(5)
214
+ }
215
+ }
216
+ ```
217
+
218
+ ---
219
+
220
+ ### `between(min, max)`
221
+
222
+ Requires input to be a number or Date within the specified bounds.
223
+
224
+ - **Parameters**
225
+ - `Ref<number> | number | Date` min
226
+ - `Ref<number> | number | Date` max
227
+ - **Works with**
228
+ - `number`, `Date`
229
+
230
+ ```js
231
+ const rules = {
232
+ numberVal: { between: between(0, 10) },
233
+ dateVal: {
234
+ between: between(new Date('12/24/2020'), new Date('12/24/2022').getTime())
235
+ }
236
+ }
237
+ ```
238
+
239
+ ---
240
+
241
+ ### `url`
242
+
243
+ Requires input to be a valid URL.
244
+
245
+ ---
246
+
247
+ ### `email`
248
+
249
+ Requires input to be a valid email address.
250
+
251
+ ---
252
+
253
+ ### `decimal`
254
+
255
+ Requires input to be a number and contain decimals.
256
+
257
+ ---
258
+
259
+ ### `hasSpecialChars`
260
+
261
+ Checks wether an input contains any special characters
262
+
263
+ ---
264
+
265
+ ### `sameAs(value, lenient)`
266
+
267
+ Requires input to be the same as the provided value in the rule.
268
+
269
+ - **Parameters**
270
+ - `Ref<any> | any` value
271
+ - `boolean` (default: false) lenient - wether to only compare based on value or value and type (eg. `==` vs `===`)
272
+ - **Works with**
273
+ - `any`
274
+
275
+ ```js
276
+ const rules = {
277
+ name: { sameAs: sameAs('Hello world') }
278
+ }
279
+ ```
280
+
281
+ ---
282
+
283
+ ### `match(regex)`
284
+
285
+ Requires value to match the provided regular expression check.
286
+
287
+ - **Parameters**
288
+ - `string | RegExp` regex
289
+ - **Works with**
290
+ - `string`
291
+
292
+ ```js
293
+ // Checks wether string contains only letters and numbers
294
+ const rules = {
295
+ name: {
296
+ noSpecialChars: match(/[^a-z0-9]/i)
297
+ }
298
+ }
299
+ ```
300
+
301
+ ---
302
+
303
+ ### `contains(items, split)`
304
+
305
+ Checks wether string input contains certain words or characters. If you write multiple words. It will check against each word individually.
306
+
307
+ - **Parameters**
308
+ - `string | string[]` items - Words to check for
309
+ - `boolean` (default: false) exact - Wether to match against the entire input string or split it by spaces. Default to true
310
+ - **Works With**
311
+ - `string`
312
+
313
+ ```js
314
+ const rules = {
315
+ value: {
316
+ // Input: 'Hello Dear World' => false
317
+ containsExact: contains('Hello World', true),
318
+ // Input: 'Hello World' => true
319
+ containsSome: contains('Hello Dear World')
320
+ }
321
+ }
322
+ ```
323
+
324
+ ---
325
+
326
+ ### `startsWith(str, pos)` & `endsWith(str, pos)`
327
+
328
+ Checks wether the input value is a string and starts or ends with the provided parameter.
329
+ You can optionally specify the position where the check begins in the input value
330
+
331
+ - **Parameters**
332
+ - `string | Ref<string>` String we are matching against
333
+ - `number` Starting position of the matching
334
+ - *Note*: When using `endsWith`, the position starts at the end and goes backwards
335
+ - **Works with**
336
+ - `string`
337
+
338
+ ```js
339
+ const rules = {
340
+ value: {
341
+ // Input 'Hello World' => true
342
+ start: startsWith('llo', 2),
343
+ // Input 'Hello World' => false
344
+ endsBad: endsWith('Hello')
345
+ }
346
+ }
347
+ ```
348
+
349
+ ## Helpers
350
+
351
+ ### `withLabel(label, rule)`
352
+
353
+ Allows you to add a custom error message to ant validation rule.
354
+
355
+ - **Parameters**
356
+ - `string | Label` label
357
+ - `ValidationRule | ValidationRuleObject` rule
358
+
359
+ ```js
360
+ const rules = {
361
+ name: {
362
+ required: withLabel('You MUST fill this input!', required)
363
+ }
364
+ }
365
+ ```
366
+
367
+ We can also use a method instead of a string when creating custom label. This method receives the validated `value` as the first parameter and all the subsequent parameters from the validation rule. If there are any.
368
+
369
+ ```js
370
+ const rules = {
371
+ name: {
372
+ between: withLabel(
373
+ (value, min, max) =>
374
+ `The value (${value}) must fit between ${min} and ${max}`,
375
+ // If error occurs, the message will read like this:
376
+ // 'The value (<value>) must fit between 5 and 10'
377
+ between(5, 10)
378
+ )
379
+ }
380
+ }
381
+ ```
382
+
383
+ ---
384
+
385
+ ### `and(rules)`, `or(rules)`
386
+
387
+ Used when we want to apply multiple rules together into one.
388
+
389
+ - `and(...ValidationRule[])` Requires all rules to be passing to resolve
390
+ - `or(...ValidationRule[])` Requires at least one rule to be passing to resolve
391
+
392
+ ```js
393
+ const rules = {
394
+ name: {
395
+ // We want
396
+ validEmail: and(decimal, minLength(10))
397
+ }
398
+ }
399
+ ```
400
+
401
+ ### `not(rules)`
402
+
403
+ Used in situations where we want to invert a rule results. Can also be used with multiple rules, only resolving when all of them are failing.
404
+
405
+ ```js
406
+ const rules = {
407
+ name: {
408
+ // We want the input value to only pass for characters
409
+ characters: not(match('[A-Za-z0-9]'))
410
+ }
411
+ }
412
+ ```
413
+
414
+ ---
415
+
416
+ ### `validateIf(condition, rule)`, `validateIfNot(condition, rule)`
417
+
418
+ Performs validation if the provided condition is met. Due to implementation limits, you can't use a rule as a condition. For that please use the `and` helper.
419
+
420
+ - **Parameters**
421
+ - `boolean | () => boolean | Ref<boolean> | Promise<boolean>` condition
422
+ - `ValidationRule` rule
423
+
424
+ ```js
425
+ const rules = {
426
+ name: {
427
+ // We want to check min length of value but only if value is an array
428
+ minLength: validateIf(Array.isArray(form.value), minLength(5)),
429
+
430
+ // If value is not a number, required it to match "Hello World"
431
+ sameAs: validateIfNot(typeof form.value === 'number', sameAs('Hello World'))
432
+ }
433
+ }
434
+ ```
435
+
436
+ ---
437
+
438
+ ### `createRule(rule, message): ValidationRule`
439
+
440
+ Helpers used to create validation rules
441
+
442
+ - **Parameters**
443
+ - `(value: any, args: Record<string, unknown>) => boolean | Promise<boolean>` rule
444
+ - `string | Label | undefined` label
445
+
446
+ ```js
447
+ // Define rule without any parameters
448
+ const required = createRule(
449
+ value => value !== undefined && value !== null,
450
+ 'Value is required'
451
+ )
452
+
453
+ // Defining rules with parameters. Returns a function which must be called when
454
+ // rule is being used
455
+ const arrAndMinLen = createRuleArg<{ length: number }> (
456
+ (value, { length }) => isArray(value) && value.length >= length,
457
+ (_, { length }) => `Array with at least ${length} length`
458
+ )
459
+
460
+ // Asynchronous rule, the main use case here is validating
461
+ // login passwords or other API related things
462
+ const checkPassword = createRule(
463
+ (password) =>
464
+ new Promise((resolve) => {
465
+ const result = await someApiCall(password)
466
+ resolve(result)
467
+ }),
468
+ 'Incorrect password. Please try again.'
469
+ )
470
+ ```