1

I have a directive appValidateOnChange that implements

@HostListener('focus') onFocus() {
    this.validators = this.formControl.control.validator;
    this.asyncValidators = this.formControl.control.asyncValidator;
    this.formControl.control.clearAsyncValidators();
    this.formControl.control.clearValidators();
}
@HostListener('change') onChange() {
    this.formControl.control.setAsyncValidators(this.asyncValidators);
    this.formControl.control.setValidators(this.validators);
    this.formControl.control.updateValueAndValidity();
}

The goal is to wait for the user to finish his input before validating it. I'm calling it simply like this

<input
        type="text"
        class="form-control"
        id="field"
        name="field"
        [ngModel]="fieldValue"
        #fieldName="ngModel"
        required
        minlength="8"
        (change)="updateField(fieldName)"
        appValidateOnChange>

In the updateField function I have

if(!field.valid)
    return false;

The problem is, there is a race condition between the two change events.

Sometimes everything works as intended, because the directive's event triggers first, and some other times validation is ignored, since the directive's event triggers second.

How can I resolve that? I would like to avoid setTimeout.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jeremy Belolo
  • 4,319
  • 6
  • 44
  • 88
  • Could you check the validity of the data on the forms submission instead of live? – Cacoon Nov 03 '17 at 07:42
  • have you tried putting a HostListener to the `input` event instead of the `change` event in the directive or the input? that way you would have a different event inside your directive and in `updateField`, instead of 2 listeners of the same event – Osman Cea Nov 03 '17 at 07:50
  • @Cacoon I would have loved to do that, sadly the specs of this project excludes forms - updates must be triggered as soon as user changes something (I hate it, tbh) – Jeremy Belolo Nov 03 '17 at 07:59
  • @OsmanCea I tried with `blur` event but it goes after `change` event and I absolutely need the update function to be called on change, not blur... The `input` event, if I'm not mistaken, triggers each time you enter a key, right ? If so it's not a good fit as validation would trigger after the first keystroke, which is not what I attend to accomplish – Jeremy Belolo Nov 03 '17 at 08:01
  • 1
    Would it be possible to move the change in component to a service that directive would run as soon as it finished onChange()? So you are sure to run both changes in the order you wish? – Vega Nov 03 '17 at 08:30
  • @Vega that could be a solution, I guess... But usually you don't want to delegate to a directive anything to do with the controller. I could do that if no other option appears, though. Thanks... – Jeremy Belolo Nov 03 '17 at 13:29

4 Answers4

2

If you have upgraded to Angular 5, a new feature was added to validate only on blur, example:

<input name="firstName" ngModel [ngModelOptions]="{updateOn: 'blur'}">

I refer you to the documentation look under the heading Angular Forms adds updateOn Blur / Submit

https://blog.angular.io/version-5-0-0-of-angular-now-available-37e414935ced

Aviad P.
  • 32,036
  • 14
  • 103
  • 124
  • Ok, that seemed very interesting, so I tried to upgrade... But encountered countless problems. There are bugs in the update way, some of my 3rd party components are not yet compatible, etc... I don't think I will be able to use Angular 5 immediately – Jeremy Belolo Nov 05 '17 at 21:25
1

I would just not show the validation result while the control has the focus and completly ignore when the default validation is happening.

Use the focus and blur events to set/clear a flag and hide the validation warning while the flag is set (using *ngIf or [hidden]="myFlag"

yurzui
  • 205,937
  • 32
  • 433
  • 399
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • But that would add 2 more event listeners by field, won't it hurt performances ? Also, I don't want validation to be displayed initially, only after the user took the pristine out of the field... I could adapt this, I guess. Initially I adapted from the answers here : https://stackoverflow.com/questions/33866824/angular2-formcontrol-validation-on-blur – Jeremy Belolo Nov 05 '17 at 21:28
  • 1
    You already have 2 event listeners. You wouldn't need to add more. It wouldn't affect performance anyway. – Günter Zöchbauer Nov 06 '17 at 04:16
  • 1
    It's working pretty well. Not exactly what I was looking for but that will do, for now. I will probably try to update Angular to 5.0 and try again the other solution that has been given to me. Thanks ! – Jeremy Belolo Nov 06 '17 at 16:46
1

I think if you use ngModelChange instead of change, as you use ngModel, then directive is always fired first:
HTML

<input myDirective
        type="text"
        class="form-control"
        id="field"
        name="field"
        [ngModel]="fieldValue"
        #fieldName="ngModel"
        required
        minlength="8"
        (ngModelChange)="updateField(fieldName)"
        appValidateOnChange>

Directive

@HostListener('ngModelChange') onChange() {
  console.log("onChange fired")
}

DEMO

Vega
  • 27,856
  • 27
  • 95
  • 103
  • 1
    Thanks, that's not bad at all... I'm still pulling away from using this directive by taking one of the two first answers, but your solution seems to work - I tested 3 times and it was always the right order. – Jeremy Belolo Nov 07 '17 at 10:20
0

I'm not sure if this is possible with template driven form. With reactive from you could do this:

template:

<input formControlName="myName" (blur)="update('myName')">

component:

update(name: string) {
    this.form.controls[name].updateValueAndValidity();
}
Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116