Skip to content

Migration Guide

Joan Pablo edited this page May 24, 2023 · 15 revisions

Breaking changes in 15.x

  • All validators have been ported over to instances of classes.
  • A new validator Validators.delegate() has been introduced to bring the possibility of creating custom validators as separate functions or methods.

In versions previous to 15.0.0 validators were functions, but after 15.0.0 all validators (i.e. synchronous and asynchronous validators) have been ported over to classes that extend from the abstract class Validator and AsyncValidator. This allows for a more consistent way of handling validators throughout the project. Individual functions|methods can still be used to create custom validators but now they must be used with the Validators.delegate() or Validators.delegateAsync() in order to provide them to the FormControls, FormGroups, and FormArrays.

Creating 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};
  }
}

Using the new validator:

final form = FormGroup({
  'acceptLicense': FormControl<bool>(
    value: false,
    validators: [
      RequiredTrueValidator(), // providing the new custom validator
    ],
  ),
});

Using Validators.delegate() with a custom function to implement a custom validator

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};
}

Breaking changes in 7.x

  • Change ReactiveFormField.validationMessages from a Map to a Function that receives the instance of the control and returns the Map with the customized validation messages.

This upgrade now brings the possibility to dynamically change the validation messages based on the current error data.

Example:

Given the form group definition:

final form = fb.group({
   'amount': [5.0, Validators.required, Validators.min(10)],
});
ReactiveTextField(
   formControlName: 'amount',
   validatationMessages: (control) => {
      ValidationMessages.required: 'The amount is required',
      if (control.hasError(ValidationMessages.min))
         ValidationMessages.min: 'The amount must have a minimum value of ${control.getError(ValidationMessages.min)['min']}'
   }
),

Breaking changes in 3.x

Set a value directly to a FormControl from code do not marks the control as touched, you must explicitly call FormControl.markAsTouched() to show up validation messages in UI. Example:

set name(String newName) {
  final formControl = this.form.control('name');
  formControl.value = newName;
  formControl.markAsTouched();// if newName is invalid then validation messages will show up in UI
}

Breaking changes in 2.x

Events renamed:

  • In AbstractControl

    • onValueChanged to valueChanges
    • onStatusChanged to statusChanges
    • onTouched to touchChanges
  • In FormGroup and FormArray

    • onCollectionChanged to collectionChanges
  • In FormControl

    • onFocusChanged to focusChanges

All events are now Streams so previous codes like the following one:

final control = FormControl<String>();

// listen to control changes
control.onValueChanged.addListener((){
  // must access the control to get the value 
  print(control.value);
});

control.value = 'Hello Reactive Forms!';

converts now to:

final control = FormControl<String>();

// listen to control changes
control.valueChanges.listen((String value){
  // the value arrives from the arguments :)
  print(value);
});

control.value = 'Hello Reactive Forms!';