GiancarloCode / form_bloc

🔥 Dart and Flutter Package 🔥 Easy Form State Management using BLoC pattern 🔥 Wizard/stepper forms, asynchronous validation, dynamic and conditional fields, submission progress, serialization and more! 🔥
https://GiancarloCode.github.io/form_bloc/
465 stars 200 forks source link

Turn on autovalidation after submit #58

Open maguro opened 4 years ago

maguro commented 4 years ago

I would like to turn on autovalidation after a submit. I also would like to validate the fields when they loose focus.

How can I accomplish this?

GiancarloCode commented 4 years ago

I will add a method to enable / disable auto validation in a few days :)

Respect validate when loose focus, I am not sure how it would be, I will try this in a few days and if it works I will add a property that allows to choose which type of validation must have the text field,

__

P.D. Respect to turn on auto validation this mean that you set autoValidete to false in the super constructor, and the use enable auto validation ?

  MyFormBloc() : super(autoValidate: false) {
    ...
  }
  void onSubmitting(){
    enableAutoValidation(); // do you want this method? 
  }
maguro commented 4 years ago

I can submit a patch, here's what I was thinking:

RaisedButton(
  onPressed: () {
    loginFormBloc.autoValidate = true;
    loginFormBloc.submit();
  },
  child: Text('LOGIN'),
),

The class FormBloc would have something like:

bool get autoValidate => _autoValidate;
set autoValidate(bool value) {
  _autoValidate = value ?? false;
  _setAutoValidateForFields(_allFieldBlocsUsed);
}

where _setAutoValidateForFields() is

void _setAutoValidateForFields(Iterable<FieldBloc> fieldBlocs) {
  FormBlocUtils.getAllFieldBlocs(fieldBlocs).forEach((e) {
    if (e is SingleFieldBloc) {
      e.add(
        AddFormBlocAndAutoValidateToFieldBloc(
            formBloc: this, autoValidate: _autoValidate),
      );
    } else if (e is ListFieldBloc) {
      e.add(
        AddFormBlocAndAutoValidateToListFieldBloc(
            formBloc: this, autoValidate: _autoValidate),
      );
    } else if (e is GroupFieldBloc) {
      e.add(
        AddFormBlocAndAutoValidateToGroupFieldBloc(
            formBloc: this, autoValidate: _autoValidate),
      );
    }
  });
}

and _onAddFieldBloc() calls _setAutoValidateForFields() as well.

GiancarloCode commented 4 years ago

Yes, more or less that's the idea, But it would be creating events for the form bloc and the public methods for this events EnableAutoValidation() DisableAutoValidation() and process them on the mapEventToState

There you can call _setAutoValidateForFields

In this way the pattern is followed and we have a record of these events in the bloc delegate

I'm not sure with the names, I always have doubts about that haha

maguro commented 4 years ago

Gotcha.

re: unfocus, in the login form,

_emailFocusNode = FocusNode()
  ..addListener(() {
    if (!_emailFocusNode.hasFocus) {
      setState(() {
        bloc.add(
            AutoValidateFieldBloc(formBloc: bloc.email, autoValidate: true));
      });
    }
  });
vasilich6107 commented 4 years ago

Autovalidation true/false This a good approach that validators are executed on each field change. This allows to track the form consistency at every moment. But the error display logic is not adjustable(correct if I'm wrong) If I have an autovalidate: true and email field(Simple example from https://giancarlocode.github.io/). Just after I enter the first symbol the form notifies me that the field is badly formatted. But I'm not done with input yet :-) How the form knows that I'm wrong?) The other case is autovalidate: false - I do not see the errors until I press 'Submit' this is a little bit annoying(from users perspective). It could lead the case that user fills long form and sees the errors far later.

This could be improved with two things - introducing the field meta data, and display error hook(callback).

Example from react-final-form(pretty proven solution)

isErrorField = (meta: Meta): boolean =>
    !!(meta && ((meta.error && meta.touched) || (meta.submitError && meta.submitFailed)));

This function checks if field has errors, if it was touched before or has submit errors and submit was failed. Returns the true/false and this is a sign whether to display an error.

Here is a link to full list of meta data of the field https://github.com/final-form/react-final-form/blob/master/src/types.js.flow#L32

How this will help.

If user hits the field for the first time the meta.touched is false. So we do not disturb the user during possible errors while he entering value for the first time. After he switches to another field meta.touched becomes true and only than we can show an error. If user gets back to the editing the meta.touched already true and now we show the realtime error display based on the assumption that the previous value was correct and if user alters it we notify him immediately that there is an error.

The default error display logic could stay the same. But with this extended meta data and error display hook everyone can alter the logic on his own demands.

ferrerogg commented 2 years ago

Any update on this? I'm also interested in disabling autovalidation until, for example, the user submits the form with some errors. I've tried to handle this use case, but I'm not sure this is the correct/best way to implement it

// MyFormBloc
@override
void onChange(Change<FormBlocState<String, String>> change) {
  super.onChange(change);

  // catch state change as with FormBlocListener.onSubmissionFailed
  if (change.nextState is FormBlocSubmissionFailed) {
    // iterate over all form fields (don't know a better way)...
    mySingleFieldBloc1.add(AddFormBlocAndAutoValidateToFieldBloc(
      formBloc: this,
      autoValidate: true,
    ));

    mySingleFieldBloc2.add(AddFormBlocAndAutoValidateToFieldBloc(
      formBloc: this,
      autoValidate: true,
    ));

     ...
  }
}
ingmferrer commented 1 year ago

My proposal: #331