Skip to content
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

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

Closed
liquidboy opened this Issue Feb 17, 2016 · 89 comments

Comments

@liquidboy
Copy link

commented Feb 17, 2016

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

This comment has been minimized.

Copy link
Contributor

commented Feb 17, 2016

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Author

commented Feb 17, 2016

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

commented May 24, 2016

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

This comment has been minimized.

Copy link

commented Jul 15, 2016

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

This comment has been minimized.

Copy link

commented Jul 19, 2016

+1 :) any news of this feature ?

@AliHoussein

This comment has been minimized.

Copy link

commented Aug 11, 2016

+1 interested as well!

@Cuel

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor

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.

@ghetolay

This comment has been minimized.

Copy link
Contributor

commented Oct 13, 2016

@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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

commented Nov 18, 2016

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Contributor

commented Dec 8, 2016

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

@SystemDisc

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

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()'},

@SystemDisc SystemDisc referenced this issue Jan 7, 2017

Closed

feat(forms): update validity on touched #13832

3 of 12 tasks complete
@Toxicable

This comment has been minimized.

Copy link
Contributor

commented Aug 1, 2017

See here for the PR on this issue #18408

kara added a commit to kara/angular that referenced this issue Aug 1, 2017

feat(forms): add updateOn blur option to FormControls
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

@vicb vicb closed this in 333a708 Aug 3, 2017

ocombe added a commit to ocombe/angular that referenced this issue Aug 5, 2017

feat(forms): add updateOn blur option to FormControls (angular#18408)
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 added a commit to asnowwolf/angular that referenced this issue Aug 11, 2017

feat(forms): add updateOn blur option to FormControls (angular#18408)
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 added a commit to juleskremer/angular that referenced this issue Aug 26, 2017

feat(forms): add updateOn blur option to FormControls (angular#18408)
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 added a commit to juleskremer/angular that referenced this issue Aug 28, 2017

feat(forms): add updateOn blur option to FormControls (angular#18408)
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

This comment has been minimized.

Copy link

commented Sep 13, 2017

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

@kara

This comment has been minimized.

Copy link
Contributor

commented Sep 13, 2017

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

@mlangwell

This comment has been minimized.

Copy link

commented Sep 14, 2017

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

commented Oct 24, 2017

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

@ghost

This comment has been minimized.

Copy link

commented Oct 24, 2017

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

@dimi-nk

This comment has been minimized.

Copy link

commented Oct 24, 2017

@marokac

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

commented Oct 24, 2017

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

@marokac

This comment has been minimized.

Copy link

commented Oct 24, 2017

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

@trotyl

This comment has been minimized.

Copy link
Contributor

commented Oct 24, 2017

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

@marokac

This comment has been minimized.

Copy link

commented Oct 24, 2017

Point taken brah ..thanks any way

@rmcsharry

This comment has been minimized.

Copy link

commented Feb 1, 2018

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

This comment has been minimized.

Copy link

commented Apr 19, 2018

this post should be awarded "most patient users ever"

@marokac

This comment has been minimized.

Copy link

commented Apr 19, 2018

Thank you

@jkrot

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

commented Jul 4, 2018

updates ?

@spyro2000

This comment has been minimized.

Copy link

commented Jul 16, 2018

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

@SystemDisc

This comment has been minimized.

Copy link

commented Aug 31, 2018

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

@marokac

This comment has been minimized.

Copy link

commented Aug 31, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.