Angular2 - FormControl Validation on blur

前端 未结 6 1730
耶瑟儿~
耶瑟儿~ 2020-11-27 14:26

I\'m looking at adding some basic email validation to check that the user has put in a correct email address. Currently using the method below, the validation updates as the

相关标签:
6条回答
  • 2020-11-27 14:44

    I have improved a little bit Alex Shestakov solution, which already worked by the way, in order to avoid setting the control state to valid when its value changes while it keeps the focus.

    @Directive({
        selector: '[validate-onblur]',
        host: {
            '(focus)': 'onFocus($event)',
            '(blur)' : 'onBlur($event)'
        }
    })
    export class ValidateOnBlurDirective {
    
        private validators: any;
        private asyncValidators: any;
        private hasFocus = false;
    
        constructor(public formControl: NgControl) {
        }
    
        onFocus($event) {
            this.hasFocus = true;
            this.validators = this.formControl.control.validator;
            this.asyncValidators = this.formControl.control.asyncValidator;
            this.formControl.control.clearAsyncValidators();
            this.formControl.control.clearValidators();
            this.formControl.control.valueChanges
                .filter(() => this.hasFocus)
                .subscribe(() => this.formControl.control.markAsPending());
        }
    
        onBlur($event) {
            this.hasFocus = false;
            this.formControl.control.setAsyncValidators(this.asyncValidators);
            this.formControl.control.setValidators(this.validators);
            this.formControl.control.updateValueAndValidity();
        }
    }
    

    This way the control will remain in pending state as long as it keeps the focus. That will help to avoid the case when a control is invalid before it gets the focus and then as soon as the user starts typing on it, it is marked as valid, before the blur event happens, when validators are set again and real validity of the control should be determined.

    0 讨论(0)
  • 2020-11-27 14:46

    Something like this: Use property touched of the ngControl object.

     <div class="form-group" [class.has-error]="!name.valid && name.touched">
            <label for="name">Name</label>
            <input #name="ngForm" ngControl="name" name="name" type="text" class="form-control" required>
     </div>
    
    0 讨论(0)
  • 2020-11-27 14:49

    Found a way, in rc6.

    1- Create a directive: validate-onblur.directive.ts

    @Directive({
      selector: '[validate-onblur]',
      host: {
        '(focus)': 'onFocus($event)',
        '(blur)': 'onBlur($event)'
      }
    })
    export class ValidateOnBlurDirective {
        constructor(public formControl: NgControl) {
        }
    
        onFocus($event) {
          this.formControl.control.markAsUntouched(false);
        }
    
        onBlur($event) {
          this.formControl.control.markAsTouched(true);
        }
    }
    

    Then in your html template just add the directive to your form, my example use the ReactiveFormsModule model.

    Then add this to your error message:

    <input type="text" formControlName="full_name" validate-onblur />
    
    <span *ngIf="formAccountDetails.controls.full_name.touched && !formAccountDetails.controls.full_name.valid && !formAccountDetails.controls.full_name.pristine" class="errors">
            ...
    </span>
    
    0 讨论(0)
  • 2020-11-27 14:59

    EDIT 2

    As Alex and the official documentation says, Angular version 5.0.0 has new option for your ngModel updateOn: 'blur'

    this.email = new FormControl(null, {
       validators: Validators.required,
       updateOn: 'blur'
    });
    

    Also you can use other update options: change (default), blur, submit.


    Original

    I use directive where remove whole validation on focus and return it back after blur event. It based on Cristian Deschamps answer.

    I update validity only on blur, so if value was invalid before focus it will be invalid after. But if you start input, validity will be updated.

    For some reasons clearing order make sense, so I clear async validators first.

    Any provided suggestion will be helpful =)

    import { Directive } from '@angular/core';
    import { NgControl } from '@angular/forms';
    
    @Directive({
      selector: '[validate-onblur]',
      host: {
        '(focus)': 'onFocus($event)',
        '(blur)': 'onBlur($event)'
      }
    })
    export class ValidateOnBlurDirective {
        private validators: any;
        private asyncValidators: any;
        constructor(public formControl: NgControl) {
        }
        onFocus($event) {
          this.validators = this.formControl.control.validator;
          this.asyncValidators = this.formControl.control.asyncValidator;
          this.formControl.control.clearAsyncValidators();
          this.formControl.control.clearValidators();
        }
    
        onBlur($event) {
          this.formControl.control.setAsyncValidators(this.asyncValidators);
          this.formControl.control.setValidators(this.validators);
          this.formControl.control.updateValueAndValidity();
        }
    }
    

    Also, please stay tuned on this Angular 2 github thread about onBlur validation


    EDIT 1

    There is another problem - if I just click on the field and after click away - validation will be called. If you have any notification about it (or server calls) - it would appear every time you do it. So you can add wasChanged property and use it like this:

        @Directive({
            selector: '[validate-onblur]',
            host: {
                '(focus)': 'onFocus($event)',
                '(blur)': 'onBlur($event)',
                '(keyup)': 'onKeyup($event)',
                '(change)': 'onChange($event)',
                '(ngModelChange)': 'onNgModelChange($event)'
            }
        })
        export class ValidationOnBlurDirective {
            private validators: any;
            private asyncValidators: any;
            private wasChanged: any;
            constructor(public formControl: NgControl) {
            }
            onFocus($event) {
                this.wasChanged = false;
                this.validators = this.formControl.control.validator;
                this.asyncValidators = this.formControl.control.asyncValidator;
                this.formControl.control.clearAsyncValidators();
                this.formControl.control.clearValidators();
            }
            onKeyup($event) {
                this.wasChanged = true; // keyboard change
            }
            onChange($event) {
                this.wasChanged = true; // copypaste change
            }
            onNgModelChange($event) {
                this.wasChanged = true; // ng-value change
            }
            onBlur($event) {
                this.formControl.control.setAsyncValidators(this.asyncValidators);
                this.formControl.control.setValidators(this.validators);
                if (this.wasChanged)
                    this.formControl.control.updateValueAndValidity();
            }
        }
    
    0 讨论(0)
  • 2020-11-27 15:01

    You may

    [class.has-error]="!form.controls['ctrlEmailAddress'].valid"
    

    This will add has-error class as soon as you will change model. So basically you don't need to blur to have validation check.

    This solved problem with angular 2.0.0 beta ;)

    0 讨论(0)
  • 2020-11-27 15:02

    As of Angular v 5.0.0 this is now possible by marking updateOn: 'blur' to the form control.

    This also means that valueChanges does not fire for that form control until the blur event occurs. Here's an example wiht minlength together with required:

    this.form = new FormGroup({
      username: new FormControl('', {
      validators: [Validators.required, Validators.minLength(6)], updateOn: 'blur'} )
    })
    
    get username() {
      return this.form.get('username');
    }
    

    In the template you'd want to mark that the validation message won't show unless the field is touched:

    <div *ngIf="username.hasError('minlength') || username.hasError('required') 
                  && username.touched">Required and minlength 6!
    </div>
    

    DEMO

    P.S If needed you can also mark this on the whole form, not only on a specific form control

    0 讨论(0)
提交回复
热议问题