Home > Enterprise >  Angular , how to handle when user click button or submit multiple times?
Angular , how to handle when user click button or submit multiple times?

Time:09-16

How can I prevent users from submitting a form multiple times? My current issue right now is when the user clicked the submit button multiple times it will create multiple users. It should create a user only once and wait before creating another user.

Here is what I have:

<button mat-flat-button color="primary" [disabled]="userStatus == 'USER_EXISTS_ON_CURRENT_ACCOUNT'" (click)="createUser()">Create
                        User</button>

TypeScript:

createUser() {
    this.accountService.create(this.modelForm.value).pipe(
      finalize(() => {
        this.isInProgress = false;
      })
    ).subscribe({next: (res) => { this.notificationService.showSuccess('User has been created successfully.');
        this._router.navigate(['settings/user']);
      },
      error: (err) => {this.notificationService.showError('Something went wrong, Try again later.');
        this.isInProgress = false;
      },
      complete: () => {
        this.isInProgress = false;
      },
    });
  }

CodePudding user response:

I have slightly updated your code,

1 - We will have to create a User button in the template And

    <button #createUserBtn mat-flat-button color="primary" [disabled]="userStatus == 'USER_EXISTS_ON_CURRENT_ACCOUNT'"> CreateUser </button>

2 - Access Create User button in .ts file

@ViewChild('createUserBtn', {static:true}) button;

3 - Create variable clicks$ to store click events

clicks$: Observable<any>;

4 - In ngOnInit: Initialize clicks$ variable to listen click events

this.clicks$ = fromEvent(this.button.nativeElement, 'click');

5 - In ngOnInit: On every click event i.e from click$ we will pass our event to exhaustMap

The beauty of exhaustMap is once the first (outer observable) event is triggered it will stop listening to events(Outer Observable) untill it completes its inner observable

So in our case when the user clicks on the button the first time(event), the exhaustMap will stop listening to the button click events until it completes our API call createUser(). This API call observable we will handle using the handleResponse() method.

ngOnInit() {
    this.clicks$ = fromEvent(this.button.nativeElement, 'click');
    
    const result$ = this.clicks$.pipe(
        tap(x => console.log('clicked.')),
        exhaustMap(ev => {
            console.log(`processing API call`);
            return this.createUser();
        })
    );
    
    result$.subscribe(this.handleResponse());
}

Create User API Call

createUser(): Observable<any> {
    return this.accountService.create(this.modelForm.value).pipe(
      finalize(() => (this.isInProgress = false))
    );
  }

To handle response

handleResponse(): any {
    return {
      next: res => {
        this.notificationService.showSuccess('User has been created successfully.');
        this._router.navigate(['settings/user']);
      },
      error: err => {
        this.notificationService.showError('Something went wrong, Try again later.');
        this.isInProgress = false;
      }
      complete: () => this.isInProgress = false;
    };
  }

Demo

If you can't access button you can move ngOnit Code to AfterViewInit Let me know if there is any error because i have not fully tested your code.

 ngAfterViewInit(): void {
    fromEvent(this.button.nativeElement, 'click')
      .pipe(
        tap(x => console.log('clicked.')),
        exhaustMap(ev => {
          console.log(`processing API call`);
          return this.createUser();
        })
      )
      .pipe(tap(x => console.log('Api call completed....')))
      .subscribe(this.handleResponse());
  }

CodePudding user response:

If the functionality you want is that the user should be restricted from clicking the button again till the API responds with a value for the prior click event, you can do the following,

In your component.html file,

<button mat-flat-button color="primary" [disabled]="isButtonDisabled()" (click)="createUser()">Create User </button>

In your component.ts file,

  • Create a boolean type variable, with initial value set to false. disableUserCreation: boolean = false;

  • Create the following function,

isButtonDisabled(): boolean {
    if (this.userStatus == 'USER_EXISTS_ON_CURRENT_ACCOUNT' || this.disableUserCreation) {
        return true;
    }
    return false;
}

Then,

createUser() {
    this.disableUserCreation = true;
    this.accountService.create(this.modelForm.value).pipe(
      finalize(() => {
        this.isInProgress = false;
      })
    ).subscribe({next: (res) => { this.notificationService.showSuccess('User has been created successfully.');
        this._router.navigate(['settings/user']);
      },
      error: (err) => {this.notificationService.showError('Something went wrong, Try again later.');
        this.isInProgress = false;
      },
      complete: () => {
        this.isInProgress = false;
        this.disableUserCreation = false;
      },
    });
}
  • Related