-
Notifications
You must be signed in to change notification settings - Fork 98
Validators
You can add validators to a FormControl as follows:
final form = FormGroup({
'name': FormControl(validators: [Validators.required]),
'email': FormControl(validators: [
Validators.required,
Validators.email,
]),
});
If at least one FormControl is invalid then the FormGroup is invalid
There are common predefined validators, but you can implement custom validators too.
- Validators.required
- Validators.requiredTrue
- Validators.email
- Validators.number
- Validators.min
- Validators.max
- Validators.minLength
- Validators.maxLength
- Validators.pattern
- Validators.creditCard
- Validators.equals
- Validators.compose
- Validators.composeOR
- Validators.contains
- Validators.any
- Validators.compare
- Validators.mustMatch
- Validators.minLength
- Validators.maxLength
- Validators.contains
- Validators.any
All validators are instances of classes that inherit from the Validator
abstract class.
In order to implement a custom validator you can follow two different approaches:
1- Extend from Validator
class and override the validate
method.
2- Or implement a custom validator function|method, and use it with the Validators.delegate(...)
validator.
Let's implement a custom validator that validates a control's value must be true
:
Let's create a class that extends from Validator
and overrides the validate
method:
/// Validator that validates the control's value must be `true`.
class RequiredTrueValidator extends Validator<dynamic> {
const RequiredTrueValidator() : super();
@override
Map<String, dynamic>? validate(AbstractControl<dynamic> control) {
return control.isNotNull &&
control.value is bool &&
control.value == true
? null
: {'requiredTrue': true};
}
}
The validator
method is a function that receives the control to validate and returns a Map
. If the value of the control is valid the function returns null
, otherwise returns a Map
with the error key and a custom information. In the previous example we have defined requiredTrue
as the error key and true
as the custom information.
In order to use the new validator class we provide an instance of it in the FormControl definition.
final form = FormGroup({
'acceptLicense': FormControl<bool>(
value: false,
validators: [
RequiredTrueValidator(), // providing the new custom validator
],
),
});
Sometimes it's more convenient to implement a custom validator in a separate method|function than in a different new class. In that case, it is necessary to use the Validators.delegate()
validator. It creates a validator that delegates the validation to an external function|method.
final form = FormGroup({
'acceptLicense': FormControl<bool>(
value: false,
validators: [
Validators.delegate(_requiredTrue) // delegates validation to a custom function
],
),
});
/// Custom function that validates that control's value must be `true`.
Map<String, dynamic>? _requiredTrue(AbstractControl<dynamic> control) {
return control.isNotNull &&
control.value is bool &&
control.value == true
? null
: {'requiredTrue': true};
}
Check the Migration Guide to learn more about custom validators after version 15.0.0 of the package.
Validator.pattern is a validator that comes with Reactive Forms. Validation using regular expressions have been always a very useful tool to solve validation requirements. Lets see how we can validate American Express card numbers:
American Express card numbers start with 34 or 37 and have 15 digits.
const AmericanExpressPattern = r'^3[47][0-9]{13}$';
final cardNumber = FormControl(
validators: [Validators.pattern(AmericanExpressPattern)],
);
cardNumber.value = '395465465421'; // not a valid number
expect(cardNumber.valid, false);
expect(cardNumber.errors.containsKey('pattern'), true);
The above code is a Unit Test extracted from Reactive Forms tests.
If we print the value of FormControl.errors:
print(cardNumber.errors);
We will get a Map like this:
{
"pattern": {
"requiredPattern": "^3[47][0-9]{13}$",
"actualValue": 395465465421
}
}
To explain what Composing Validators is, let's see an example:
We want to validate a text field of an authentication form. In this text field the user can write an email or a phone number and we want to make sure that the information is correctly formatted. We must validate that input is a valid email or a valid phone number.
final phonePattern = '<some phone regex pattern>';
final form = FormGroup({
'user': FormControl<String>(
validators: [
Validators.composeOR([
Validators.email,
Validators.pattern(phonePattern),
])
],
),
});
Note that Validators.composeOR receives a collection of validators as argument and returns a validator.
With Validators.composeOR we are saying to FormControl that if at least one validator evaluate as VALID then the control is VALID it's not necessary that both validators evaluate to valid.
Another example could be to validate multiples Credit Card numbers. In that case you have several regular expression patterns for each type of credit card. So the user can introduce a card number and if the information match with at least one pattern then the information is considered as valid.
final form = FormGroup({
'cardNumber': FormControl<String>(
validators: [
Validators.composeOR([
Validators.pattern(americanExpressCardPattern),
Validators.pattern(masterCardPattern),
Validators.pattern(visaCardPattern),
])
],
),
});