Angular 2 form validators messing with the cancel button
Asked Answered
C

5

7

I have a data gathering component which includes 'cancel' button to cancel the whole process. The problem is, if some of the HTML input fields which are validated by Angular 2 validators have focus, and are not valid, and I press the cancel button, the component is not removed. Instead, validators will fire and the cancel button press will be ignored. I have to press it for the second time, after the validators complain, to make the component disappear.Cancel button itself simply triggers routing away from the component. Relevant code:

component.html

<form [formGroup]="addReminderForm" (ngSubmit)="onSubmit(addReminderForm.value)">
  <input type="text" [formControl]="addReminderForm.controls['text']" />
  <div class="error" *ngIf="addReminderForm.controls['text'].hasError('required') &&
  addReminderForm.controls['text'].touched">You must enter reminder text</div>

  <button type="submit" [disabled]="!addReminderForm.valid" >Add Reminder</button>
</form>
<button (click)="cancel()">Cancel</button>

component.ts:

ngOnInit() {
  this.addReminderForm = this.fb.group({  
      'text': ['', Validators.compose([Validators.required, Validators.maxLength(20)])]
  });
}
cancel() {
    // Simply navigate back to reminders view
    this.router.navigate(['../'], { relativeTo: this.route }); // Go up to parent route     
 }

I have no idea why this happens. Any ideas?

Canescent answered 10/7, 2017 at 14:58 Comment(4)
Similar question, showing same problem occurs in Modal forms with a cancel button: #46411890Eadmund
Thanks, I noticed that question. When I think about the issue, my form actually had handlers for blur event-it trimmed the input field on blur. I now wonder if this is the cause?Canescent
No, that's not the cause. The cause is that form validation fires as soon as blur fires. So it happens before the cancel click is registered. I will post an answer on how to solve it.Eadmund
Did you find a solution in the end?Stubby
E
12

Change the event from (click) to (mousedown). Mousedown is invoked before blur event.

So instead of this <button (click)="cancel()">Cancel</button>

try this: <button (mousedown)="cancel()">Cancel</button>

Engel answered 13/9, 2018 at 8:53 Comment(3)
I can't check this right now, but I'm sure this is the true solution to the problem.Canescent
@Engel - Thank you!Hofuf
This is not accessible, (mousedown) will not fire when a user presses space or enter. Use keydown actions to keep this behaviour if you choose this approach.Debor
R
4

Try using button type="reset" like:

<form [formGroup]="heroForm" (ngSubmit)="onSubmit()" novalidate>

  ...       
  <div>
    <button type="submit" [disabled]="heroForm.pristine">Save</button>
    <button type="reset" (click)="revert()"[disabled]="heroForm.pristine">Revert o Cancel</button>
  </div>

</form>

In your component class:

revert() { this.ngOnChanges(); }

For more information in https://angular.io/guide/reactive-forms

I hope to help you.

Roybal answered 10/7, 2017 at 15:35 Comment(1)
I am not trying to reset the form, but to completely cancel the whole component (remove it). I did try to declare the button type as reset, but nothing changed.Canescent
E
1

The cause is that form validation fires as soon as the blur event fires. So it happens before the cancel click is registered.

I can only figure out one workaround - have a flag in your component that is initially set to false to know when to show the errors.

Only set it to true when you want validations to 'fire'.

So if the form loads and the user clicks in a field and then clicks cancel, the flag is still false and so no validations show. If the user clicks submit you turn the flag on, causing validations to be shown.

For example, this form has a span block that is used to show validation errors and it is protected by and *ngIf for the flag. Also protected is the 'checking-field' class for the input itself - this just adds a red border around the input if there is an error. So the flag must be true for the red border and the validation errors to show up.

  <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
    <div class="modal-body">
      <input #nameElement id="template-name" (blur)="onBlur($event.target)" 
        [formControl]="name" (keyup)="keyUp($event)" class="form-control"
        [ngClass]="{'checking-field': isShowError}" 
        placeholder="Name this template">
      <span class="help-block" *ngIf="isShowError">
        <span class="text-danger" *ngIf="form.get('name').hasError('minlength')">Name is required</span>
        <span class="text-danger" *ngIf="form.get('name').hasError('noDifference')">Name must be different</span>
      </span>
    </div>
  </form>

OnSubmit starts like this:

  onSubmit(data: any):void {
    this.isShowError = true;
    if (this.form.valid) ...

which ensure validation errors will show when user clicks submit. They WON'T show up when a field loses focus, because the flag will be false.

However, you MAY be able to get round that by an ugly hack, as follows (cumbersome also since you then need a blur handler for all fields on the form!).

Add another flag to track when the user clicks the cancel button, and for each field on the form, in the (blur) handler, check it within a timeout, like this:

setTimeout(() => {if (!userClickedCancel) this.isShowError = true}, 500);

Then in the cancel click handler:

this.userClickedCancel = true;

This means the cancel code should change the flag before the timeout expires in the handlers to check it's value.

** NOTE: If you are 'dismissing' the form and not navigating away **

Also, if you are not navigating away but maybe 'hiding' the form (or closing a modal), you should also set the isShowError flag to false in the cancel handler (for cases where user hits submit, form is invalid, then they click cancel, and then you reload the form again).

Eadmund answered 6/2, 2018 at 19:39 Comment(0)
Q
1

What you can do is to change the button to a good, old fashioned

    <input type="submit" value="submit" .... /> tag.

and change the cancel button to:

    <input type="button" value="cancel" (click)="your event here" />

The submit button will trigger the form since you're already making a call to the submit event.

This should solve your problem or anyone else that is having this issue.

Quadrennial answered 2/1, 2020 at 16:2 Comment(0)
L
0

I know I am pretty much late but it might help someone!

If anyone is using the ReactiveForm and add a clear button inside the formGroup and using the click method then it will clear the form certainly! but it will also submit the blank form to the server! to prevent this I use 2 methods!

1. Use <i> tag instead of <button>
2. use type = "Reset"

In both cases, on click cancel button it wont call server API.

Example:

 <button class="example-button-row" type="reset" mat-flat-button color="primary"
        (click)="sendEmailGroup.reset()"><i class="material-icons">cleaning_services</i> Clear </button>
Lucillalucille answered 17/12, 2021 at 17:35 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.