Skip to content

Angular2 Forms Controls validation "OnLostFocus (OnBlur)" rather than "OnChange" #7113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
liquidboy opened this issue Feb 17, 2016 · 90 comments

Comments

@liquidboy
Copy link

Hi,

Looking at the code for setting up form controls in shared.ts it appears we detect control changes onChange "registerOnChange"

eg. if a user types into an input control that is an ng2 angular control, each keypress triggers a validationcheck ..

It appears to be a big change if i were to change it from OnChange to something like LostFocus of the control ...

The reason i ask is business really doesn't like the validation messages appearing suddenly even while the user hasn't finished filling in the field ... eg. filling in a "Password" control, the user is suddenly shown validation errors while they're typing ...

Anyone got a nice solution for hiding validation messages and only showing them once they tab off the control ?!

@gbataille
Copy link
Contributor

That's interesting.
So the class valid/dirty/invalid seems nice (I guess you can agree with the decorator that say the field is not yet compliant).
However I agree that the messages are more cumbersome and sometimes obvious enough (like on a login screen)

The problem is when to display them then? how to know the user is "done"

For example I have a form that disables the submit button if the form is invalid. If it's the last field that is invalid, the user is editing it. He can't click on the button, so the "lost focus" event might not come depending on the user behaviour.

Let me throw a couple of UX ideas that use form validation on each change, but that react a bit differently (async ?) to it:

  • display the errors/details after some timer of inactivity (a bit heavy)
  • display the errors/details in a way that does not disturb the form layout (in a side floating tooltip rather than in a div that appear/disappear below each input)

what do you think?

@zoechi
Copy link
Contributor

zoechi commented Feb 17, 2016

I think the issue is just about being able to use the blur event instead of the change event for calling the validator, so validation is only done when the user is done editing a field.

display the errors/details in a way that does not disturb the form layout (in a side floating tooltip rather than in a div that appear/disappear below each input)

Shouldn't bee too hard. This is IMHO only a CSS problem not an Angular problem.

display the errors/details after some timer of inactivity (a bit heavy)

Shouldn't be too hard to implement. Just create an additional flag used in *ngIf to show/hide validation errors. Subscribe to form changes and if there is some delay toggle the flag.

<div class="validation-message" *ngIf="form.controls['xxx'].isValid && isIdle">

@liquidboy
Copy link
Author

thanks guys ... ill try out the recommendation from zoechi ...

our interaction designers are very adamant that the UI should NOT be changing whilst the user is entering data .. apparently they hate any form of distraction for the user during their data entry interaction..

@ps2goat
Copy link

ps2goat commented Mar 17, 2016

+1 to have the option to validate on blur vs on change. If blur, don't mark an input as dirty unless the value has changed and the user has moved focus out of the input.

@JohnnyClutch
Copy link

The onBlur validation is definitely something I occasionally used in Angular 1, and will be implementing via a "controller" function if no provision is made by Angular 2. +1

@liquidboy liquidboy changed the title Angular2 Forms Controls validation "OnLostFocus" rather than "OnChange" Angular2 Forms Controls validation "OnLostFocus (OnBlur)" rather than "OnChange" Jun 15, 2016
@bruceauyeung
Copy link

hi everyone !
i wanna know whether this validate-onblur mechanism has been implemented in @angular/forms 0.2.0 ? if not, is there any workaround to do this ?

@BBegouin
Copy link

+1 :) any news of this feature ?

@AliHoussein
Copy link

+1 interested as well!

@Cuel
Copy link
Contributor

Cuel commented Sep 28, 2016

Ran into this issue myself, would be nice if the form controls could report if their input is currently active/focused

@kara
Copy link
Contributor

kara commented Oct 13, 2016

@liquidboy If the concern is that the error messages are being displayed as the user types (too early), have you considered using the touched property? A control is only marked touched on the blur event, so the error message would not appear until then.

<div class="error" *ngIf="email.touched && email.invalid".
   Email is invalid!
</div>
<input name="email" ngModel #email="ngModel"> 

What we don't have yet is support for actually running the validation on blur instead of on value change. Besides error message display, what are some other use cases for this? Trying to get a read on priority.

@kara kara added feature Issue that requests a new feature workaround1: obvious freq3: high labels Oct 13, 2016
@ghetolay
Copy link
Contributor

@kara this will only work once. If user reset or manually delete the input then problem is back.

But thks it's still better and will do that from now on :)

@kara
Copy link
Contributor

kara commented Oct 13, 2016

@ghetolay Resetting will actually set the field back to "untouched", so you shouldn't run into that problem. But you're right that if the user leaves the input, then comes back and deletes it, it will still be "touched".

@Cuel
Copy link
Contributor

Cuel commented Oct 13, 2016

Telling the user that they need at least 4 characters before they've even had a chance to enter those 4 a badly designed UI. It works for maxLength because then we can be certain something is wrong. Not the same story for minLength or even pattern.

Fiddling with touched works, but it feels like a chore to fiddle it back and forth and it doesn't "feel" like the correct way to do it - developers don't know what touched might be used for internally or by e.g directives. In my app at the moment I have validators: ValidatorFn[] and onChangeValidators: ValidatorsFn[] when creating a FormControl. All input components have an onChange and later on that uses setError to push the validator error in. It works quite well so it's certainly possible to get around this issue.

There's a few quirks introduced because editing a field and instantly clicking on a submit button might allow the event to trigger (e.g when used with disabling buttons), so I have to manually ask the FormModel if it's valid (allow it to update).

On top of that we have a view with quite a complex UI with over 50 inputs divided into 5-6 components. All components have to react on changes in the data in other components to update themselves with other inputs. We thought about having a store and using valueChanges to update the data but it didn't feel like a very performant solution - updating it onblur/change would be better.

@Longfld
Copy link

Longfld commented Oct 18, 2016

Ok, I found a way to do this as:

<input type="text" required #mycontrol formControlName="MYCTRL" (change)="MYCTRL=mycontrol.value;Update('MYCTRL')" />

both MYCTRL and Update() as part of model.
but, to be honestly, I would like to have something like onModuleBlur to pass job to ng2 , rather I call (change).

Well, the reason I come here as I need find out how to validate/show message (:

I need this "onModuleBlur" or BlueChanges( just like ValueChanges) to convert my site from angular 1 to 2, because my site is bit of strange as, after user changes controlValue, some business rules will decide if this change has to post to backend or... before user does anything else.

@fxck
Copy link

fxck commented Oct 19, 2016

@kara imagine you have an email input, which is asynchronously checking whether it's unique or not... now no matter how much you debounce the checking event, it never ever makes sense to check until the user is done, which for me is when he blurs the input.

@kalyan1102
Copy link

kalyan1102 commented Nov 16, 2016

Think from accessibility perspective, a screen reader user typing on the field and hearing repeated alert that the form field is invalid. This would be a nightmare for a screen reader user. Providing a solution to validate onBlur would be very helpful... Is there any update when we will get one?

@mattdistefano
Copy link

I'll second the scenario @fxck cited - IMO async validation will more often than not need to be fired on blur. We hacked around this in our app by having our async validators return a different error when focused and filtering that off in our display logic, but it's kind of ugly.

I will add I think this setting should be specified on a per-use basis rather than per-validator-type or per-form.

@kalyan1102 In my experience, the error message will only be announced when the DOM element is added/shown (assuming you're using aria-live="assertive") so you shouldn't hear it with each keystroke.

@dsoldera
Copy link

dsoldera commented Dec 5, 2016

Hi.

It's a pity that anyone have worked on this issue yet. I think Reactive Forms are great and I want to use on my project with the OnLostFocus event. Please consider to improve this issue ASAP.

@vicb
Copy link
Contributor

vicb commented Dec 8, 2016

@dsoldera feel free to help, almost any contribution is welcome !

@SystemDisc
Copy link

SystemDisc commented Jan 7, 2017

Would this work, or should there be ways to configure it? Let me know what's needed and I'll make a PR.

  dir.valueAccessor.registerOnTouched(() => {
    control.updateValueAndValidity();
    control.markAsTouched();
  });

dir.valueAccessor.registerOnTouched(() => control.markAsTouched());

@SystemDisc
Copy link

SystemDisc commented Jan 7, 2017

I'm pretty sure that, since onTouched() is called for (blur), all that needs to be done is the change noted in my previous comment.

host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},

@vicb vicb closed this as completed in 333a708 Aug 3, 2017
ocombe pushed a commit to ocombe/angular that referenced this issue Aug 5, 2017
By default, the value and validation status of a `FormControl` updates
whenever its value changes. If an application has heavy validation
requirements, updating on every text change can sometimes be too expensive.

This commit introduces a new option that improves performance by delaying
form control updates until the "blur" event.  To use it, set the `updateOn`
option to `blur` when instantiating the `FormControl`.

```ts
// example without validators
const c = new FormControl(, { updateOn: blur });

// example with validators
const c= new FormControl(, {
   validators: Validators.required,
   updateOn: blur
});
```

Like in AngularJS, setting `updateOn` to `blur` will delay the update of
the value as well as the validation status. Updating value and validity
together keeps the system easy to reason about, as the two will always be
in sync. It's  also worth noting that the value/validation pipeline does
still run when the form is initialized (in order to support initial values).

Closes angular#7113
asnowwolf pushed a commit to asnowwolf/angular that referenced this issue Aug 11, 2017
By default, the value and validation status of a `FormControl` updates
whenever its value changes. If an application has heavy validation
requirements, updating on every text change can sometimes be too expensive.

This commit introduces a new option that improves performance by delaying
form control updates until the "blur" event.  To use it, set the `updateOn`
option to `blur` when instantiating the `FormControl`.

```ts
// example without validators
const c = new FormControl(, { updateOn: blur });

// example with validators
const c= new FormControl(, {
   validators: Validators.required,
   updateOn: blur
});
```

Like in AngularJS, setting `updateOn` to `blur` will delay the update of
the value as well as the validation status. Updating value and validity
together keeps the system easy to reason about, as the two will always be
in sync. It's  also worth noting that the value/validation pipeline does
still run when the form is initialized (in order to support initial values).

Closes angular#7113
juleskremer pushed a commit to juleskremer/angular that referenced this issue Aug 26, 2017
By default, the value and validation status of a `FormControl` updates
whenever its value changes. If an application has heavy validation
requirements, updating on every text change can sometimes be too expensive.

This commit introduces a new option that improves performance by delaying
form control updates until the "blur" event.  To use it, set the `updateOn`
option to `blur` when instantiating the `FormControl`.

```ts
// example without validators
const c = new FormControl(, { updateOn: blur });

// example with validators
const c= new FormControl(, {
   validators: Validators.required,
   updateOn: blur
});
```

Like in AngularJS, setting `updateOn` to `blur` will delay the update of
the value as well as the validation status. Updating value and validity
together keeps the system easy to reason about, as the two will always be
in sync. It's  also worth noting that the value/validation pipeline does
still run when the form is initialized (in order to support initial values).

Closes angular#7113
juleskremer pushed a commit to juleskremer/angular that referenced this issue Aug 28, 2017
By default, the value and validation status of a `FormControl` updates
whenever its value changes. If an application has heavy validation
requirements, updating on every text change can sometimes be too expensive.

This commit introduces a new option that improves performance by delaying
form control updates until the "blur" event.  To use it, set the `updateOn`
option to `blur` when instantiating the `FormControl`.

```ts
// example without validators
const c = new FormControl(, { updateOn: blur });

// example with validators
const c= new FormControl(, {
   validators: Validators.required,
   updateOn: blur
});
```

Like in AngularJS, setting `updateOn` to `blur` will delay the update of
the value as well as the validation status. Updating value and validity
together keeps the system easy to reason about, as the two will always be
in sync. It's  also worth noting that the value/validation pipeline does
still run when the form is initialized (in order to support initial values).

Closes angular#7113
@jagomf
Copy link

jagomf commented Sep 13, 2017

@vicb I wouldn't close this issue until template-driven forms are supported as well...

@kara
Copy link
Contributor

kara commented Sep 13, 2017

@jagomf Template-driven forms are already supported. See #18577 and #18594

@mlangwell
Copy link

Anyone able to get this to work? I have been trying without success.

This is my form so far:

this.infoForm = this.fb.group({
      userName: [this.regServ.userName, Validators.required, { validators: userExists.bind(this), updateOn: blur }],
      email: [this.regServ.email, Validators.compose([Validators.required, verifyEmail.bind(this)])],
      confirmEmail: ['', Validators.compose([Validators.required, verifyEmail.bind(this)])],
      password: ['', Validators.required],
      confirmPassword: ['', Validators.required],
    });

When I type into the userName control it throws a "TypeError: v is not a function"

@kara
Copy link
Contributor

kara commented Sep 14, 2017

@mlangwell FormBuilder doesn't yet support updateOn (see #19163). If you switch to creating the FormGroup yourself, it should work. Worth noting that the control options should be passed as the second arg, not the third though.

@mlangwell
Copy link

mlangwell commented Sep 15, 2017

@kara thank you for clearing that up!

Regarding the third arg, isn't the third arg for asyncValidators? My third arg for userName is an async check on our API if a user name already exists. This was the only way I could get the async validator to work with a sync validator on the same control. If there is a better way I am open to suggestions!

Also I just tried testing it with the FormControl. To keep it simple I kept it as close to the example as possible and it is throwing me an error as well.

userName = new FormControl(this.regServ.userName, { validators: Validators.required, updateOn: blur });

That is what I have and this is the error

Argument of type '{ validators: (control: AbstractControl) => ValidationErrors; updateOn: () => void; }' is not assignable to parameter of type 'ValidatorFn | ValidatorFn[]'.
  Object literal may only specify known properties, and 'validators' does not exist in type 'ValidatorFn | ValidatorFn[]'.

@kara
Copy link
Contributor

kara commented Sep 15, 2017

What version are you using? It's only available on the 5.x beta versions. See example here: http://plnkr.co/edit/Ig8XG1rTll4FwW6gVcqK?p=preview.

Async validators are set as a separate option. See docs here: https://next.angular.io/api/forms/FormControl.

@mlangwell
Copy link

mlangwell commented Sep 18, 2017

@kara our team is using 4.3.6, so that would be why it does not work. We are not permitted to use any beta versions unfortunately.

The only way I could get this to work was to add a blur event to the input control and then call setErrors() on the control as appropriate. This worked for me if anyone else needs the work around this is what it looks like:

<input class="form-control" formControlName="userName" placeholder="User Name" type="text" (blur)="checkUserExists()"/>
<div class="alert-danger" *ngIf="userName.invalid && userName.touched">
  <div *ngIf="userName.hasError('required')">User Name is required</div>
  <div *ngIf="userName.hasError('userExists')">{{userName.errors.userExists}}</div>
</div>

checkUserExists() {
    if (this.userName.value) {
      this.regServ.userNameExists(this.userName.value)
      .subscribe((exists) => {
        if (exists) {
          this.userName.setErrors({ userExists: `User Name "${this.userName.value}" already exists` });
        }
      });
    }
  }

@marokac
Copy link

marokac commented Oct 24, 2017

Yoo guys on githup you never solve anything , stackOverflow is even better

@ghost
Copy link

ghost commented Oct 24, 2017

@marokac that must explain the 10,000+ closed issues in this repo.

@dimi-nk
Copy link

dimi-nk commented Oct 24, 2017

@marokac
Copy link

marokac commented Oct 24, 2017

Thus because i been struggling with the sane issue here. maybe you can help. I am trying to validate a form using angular formBuilder..here is my code:

ngOnInit() {
this.formBuilder = new FormBuilder();
this.simpleForm = this.formBuilder.group({
notifyMeRadio: [],
notifyMeEmail: [this.notifyMeEmail,[CommonValidators.isValidEmailAddress]],
notifyMeSMS: [this.notifyMeSMS, [Validators.minLength(10)]],
notifyMeFax: [this.notifyMeFax,[CommonValidators.isValidBeneficiaryName]],
notifyBeneficiaryRadio: [''],
notifyBeneficiaryEmail: [this.notifyBeneficiaryEmail,[CommonValidators.isValidEmailAddress]],
notifyBeneficiarySMS: [this.notifyBeneficiarySMS,[Validators.minLength(10)]],
notifyBeneficiaryFax: [this.notifyBeneficiaryFax,[Validators.minLength(10)]],
});
this.simpleForm.valueChanges.subscribe(data => this.onValueChanged(data));
this.onValueChanged();

And my validation messages object goes like this:

validationMessages = {
'notifyMeSMS': {'required':'field cannot be empty',
'isValidEmailAddress':'10 digist'
},
'notifyMeEmail': {'required': 'Please enter valid Email address'},
'notifyMeFax' : { 'required': 'Please enter valid Fax name',
'isValidBeneficiaryName':'isValidBeneficiaryName'
},

'notifyBeneficiarySMS':  {'required':'Please enter valid

SMS', 'isValidEmailAddress':'please type a valid email address'},
'notifyBeneficiaryEmail': {'required':'required field'},
'notifyBeneficiaryFax' : {'required':'Please enter valid beneficiary Fax'},
};

Form errors here:

formErrors: {
'notifyMeRadio':'',
'notifyMeEmail': '',
'notifyMeSMS': '',
'notifyMeFax': ''
},

And finally my OnvalueChange method:

onValueChanged(data?: any) {
if (!this.simpleForm) { return; }
const form = this.simpleForm;

for (const field in this.formStatus.formErrors) {

  //this.formStatus.formErrors[this.formStatus.formErrors.notifyMeSMS] = ''
  this.formStatus.formErrors[field] = '';
  const control = form.get(field)
  if (control && control.dirty && !control.valid) {
    const messages = this.validationMessages[field];
    for (const key in control.errors) {
    this.formStatus.formErrors[field] +=messages[key] + ' '
   
    
    }
  }
}

}

My issue here is i can only get validation messages when i include required and it only return required only

please help guys

@ghost
Copy link

ghost commented Oct 24, 2017

@marokac you should ask on StackOverflow. They're so much better than us guys.

@marokac
Copy link

marokac commented Oct 24, 2017

@notsonotso I will beg you on this one my man please help if you can...

@trotyl
Copy link
Contributor

trotyl commented Oct 24, 2017

@marokac GitHub issue is not used for support request, please refer to CONTRIBUTING for more information.

@marokac
Copy link

marokac commented Oct 24, 2017

Point taken brah ..thanks any way

@rmcsharry
Copy link

Validations firing onBlur appear to conflict with having a 'Cancel' button for the form. Cancel is not like a form reset, it is a 'total dismiss' of the input/edit operation, possibly even closing the form.

If a field has focus, then the validation logic fires when the field loses focus and before the Cancel click occurs. This is particularly a bad UX when the form is inside a modal - since the Cancel button also should dismiss the modal, except it cannot because of the validation firing first.

SO example here.

I believe in Angular 1.x we could set form.submitted = false inside the Cancel click handler, but in Angular 2+ submitted is a read-only attribute and cannot be set.

So how can we 'form cancel' without firing any validations?

@steelx
Copy link

steelx commented Apr 19, 2018

this post should be awarded "most patient users ever"

@marokac
Copy link

marokac commented Apr 19, 2018

Thank you

@jkrot
Copy link

jkrot commented Apr 24, 2018

I would still like this as well because doing silly things with render to get this to happen doesn't make me happy.

@imadhy
Copy link

imadhy commented Jul 4, 2018

updates ?

@spyro2000
Copy link

Why isn't that just a global setting or something?

@SystemDisc
Copy link

Someone come up with a solid proposal that we can agree on. Then, a PR can be made implementing that proposed solution.

@marokac
Copy link

marokac commented Aug 31, 2018

I think we can fire the blur event only when click happens inside the form

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.