joanpablo / reactive_forms

This is a model-driven approach to handling form inputs and validations, heavily inspired in Angular's Reactive Forms
MIT License
469 stars 87 forks source link

MustMatchValidator not working with ReactiveFormsGenerator & Freezed #404

Open Dionnie123 opened 1 year ago

Dionnie123 commented 1 year ago
@freezed
@Rf()
class RegisterForm with _$RegisterForm {
  // ignore: invalid_annotation_target
  @JsonSerializable(fieldRename: FieldRename.snake)
  factory RegisterForm({
    @RfControl(validators: [RequiredValidator()]) String? fullName,
    @RfControl(validators: [RequiredValidator()]) String? email,
    @RfControl(validators: [RequiredValidator()]) String? password,
    @RfControl(validators: [
      RequiredValidator(),
      MustMatchValidator(
        'password',
        'passwordConfirmation',
        true,
      )
    ])
    String? passwordConfirmation,
    @RfControl(validators: [RequiredTrueValidator()]) bool? acceptLicense,
  }) = _RegisterForm;

  factory RegisterForm.fromJson(Map<String, dynamic> json) =>
      _$RegisterFormFromJson(json);
}
RegisterFormFormBuilder(builder: (x, f, c) {
                        return Padding(
                          padding: const EdgeInsets.all(25.0),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              const BoxText.headline('SIGN UP'),
                              const BoxText.caption('Enter your details below'),
                              verticalSpaceMedium,
                              ReactiveTextField<String>(
                                formControl: f.fullNameControl,
                                validationMessages: {
                                  ValidationMessage.required: (_) => 'Required',
                                },
                                textInputAction: TextInputAction.done,
                                decoration: const InputDecoration(
                                  labelText: "Fullname",
                                  helperText: '',
                                  helperStyle: TextStyle(height: 0.8),
                                  errorStyle: TextStyle(height: 0.8),
                                ),
                              ),
                              const SizedBox(height: 8.0),
                              ReactiveTextField<String>(
                                formControl: f.emailControl,
                                validationMessages: {
                                  ValidationMessage.required: (_) => 'Required'
                                },
                                decoration: const InputDecoration(
                                  labelText: 'Email',
                                  helperText: '',
                                  helperStyle: TextStyle(height: 0.8),
                                  errorStyle: TextStyle(height: 0.8),
                                ),
                              ),
                              const SizedBox(height: 8.0),
                              ReactiveTextField<String>(
                                formControl: f.passwordControl,
                                //   obscureText: true,
                                validationMessages: {
                                  ValidationMessage.required: (_) => 'Required'
                                },
                                textInputAction: TextInputAction.done,
                                decoration: const InputDecoration(
                                  labelText: "Password",
                                  helperText: '',
                                  helperStyle: TextStyle(height: 0.8),
                                  errorStyle: TextStyle(height: 0.8),
                                ),
                              ),
                              const SizedBox(height: 8.0),
                              ReactiveTextField<String>(
                                formControl: f.passwordConfirmationControl,
                                //   obscureText: true,
                                validationMessages: {
                                  ValidationMessage.required: (_) => 'Required',
                                  ValidationMessage.mustMatch: (_) =>
                                      "Not match",
                                },
                                textInputAction: TextInputAction.done,
                                decoration: const InputDecoration(
                                  labelText: "Confirm Password",
                                  helperText: '',
                                  helperStyle: TextStyle(height: 0.8),
                                  errorStyle: TextStyle(height: 0.8),
                                ),
                              ),
                              Row(
                                children: [
                                  ReactiveCheckbox(
                                    formControl: f.acceptLicenseControl,
                                  ),
                                  const Text("Accept Privacy and Policy")
                                ],
                              ),
                              verticalSpaceMedium,
                              ReactiveRegisterFormFormConsumer(
                                builder: (context, formModel, child) {
                                  return BoxButton(
                                    title: 'SIGN UP',
                                    disabled: f.form.hasErrors ? true : false,
                                  );
                                },
                              ),
                            ],
                          ),
                        );
                      }

image

vasilich6107 commented 1 year ago

Could you create issues in dedicated repo for reactive forms generator https://github.com/artflutter/reactive_forms_generator

vasilich6107 commented 1 year ago

Check examples to understand how to use must match https://github.com/artflutter/reactive_forms_generator/blob/master/packages/reactive_forms_generator/example/lib/docs/login/login.dart

Dionnie123 commented 1 year ago

Thank you for quick response @vasilich6107 I solved the problem by creating a custom MustMatchValidator (the built-in one isn't working for me )

class MustMatchValidatorZ extends Validator<dynamic> {
  final String controlName;
  final String matchingControlName;
  final bool markAsDirty;
  const MustMatchValidatorZ(
      this.controlName, this.matchingControlName, this.markAsDirty)
      : super();

  @override
  Map<String, dynamic>? validate(AbstractControl<dynamic> control) {
    final error = {ValidationMessage.mustMatch: true};
    var form = {};
    control.parent?.valueChanges.listen((event) {
      form = event as Map<dynamic, dynamic>;

      if (form[controlName] != form[matchingControlName]) {
        control.setErrors(error, markAsDirty: markAsDirty);
        control.markAsTouched();
      } else {
        control.removeError(ValidationMessage.mustMatch);
      }
    });

    return null;
  }
}

image

I'm not just sure if it's a good solution, but it's working smoothly!

joanpablo commented 1 year ago

Hi @Dionnie123,

Thanks for using Reactive Forms.

The MustMatchValidator is intended to be used only at FormGroup level not at the FormControl level. Try using the built-in MustMatchValidator at FormGroup level.

vasilich6107 commented 1 year ago

Hi @joanpablo We can close this issue