Issue
Update 1: Based on answer from @PierreDuc, I have forked and created a simpler version here: Simplified Example. Man, I remember in AngularJS being able to totally hijack the (click) function and execute it. I get it, I get it, I like the simplified proposal below though. That should work for me :)
I have created an Angular button directive that accepts a method as a Promise.
On click, I disable the button.  When the Promise is resolved I then re-enables the button.  
Basically, on click I want to disable the button until all processing that button event is performing completes, including any http calls.
I have accomplished my goal seen here: Stackblitz Example.
I don't particularly like my "solution".
It's overly complicated. In order to get it to work three things need to happen.
- The directive be added to the button.
- The "waitFor" property be set.
- The "waitFor" function must be a function that returns a Promise.
IMO, that's too many things that need to align to get it work.
What I would really like to do is get a handle on the (click) method of the button and execute it manually in the directive the way I did my "waitFor" property.
How can I do this?
At the very least, I would like a directive that doesn't require both the directive name ("appClickWait") and the property ("[waitFor]").
Here is the code for your convenience:
Directive:
import { Directive, HostListener, ElementRef, Output, Input, EventEmitter, Renderer2 } from '@angular/core';
// Enfore this directvie can only be used on a button?
@Directive({
  selector: '[appClickWait]'
})
export class ClickWaitDirective {
  @Input("waitFor") clickWait: any;
  constructor(private el: ElementRef, private renderer: Renderer2) { }
  @HostListener('click', ['$event'])
  clickEvent(event) {
    event.preventDefault();
    event.stopPropagation();
    this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'disabled');
    const originalInnerText = this.el.nativeElement.innerText;
    this.el.nativeElement.innerText = 'Processing...';
    const reset = () => {
      this.renderer.removeAttribute(this.el.nativeElement, 'disabled');
      this.el.nativeElement.innerText = originalInnerText;
    };
    // I really would like to just get a handle on the (click) function here
    // that would greatly simplify the useage of this directive
    this.clickWait()
      .then((data) => {
        reset();
      })
      .catch(err => {
        console.error(err);
        reset();
      });
  }
}Template:
  myClickFunc = async () => {
    console.log('start')
    this.posts = [];
    // too fast, let's wait a bit
    // this.posts = await this.http.get('https://jsonplaceholder.typicode.com/posts').toPromise();
    await new Promise((resolve, reject) => {
      setTimeout(async () => {
        try {
          this.posts = await this.http.get('https://jsonplaceholder.typicode.com/posts').toPromise();
        } catch (err) {
          reject(err);
        }
        resolve();
      }, 1000);
    });
    console.log('all done');
  }<button type="button" appClickWait [waitFor]="myClickFunc">Single Wait Click</button>Thank you!
Solution
I suppose you can simplify it to this:
@Directive({
   selector: 'button[appClickWait]'
})
export class ClickWaitDirective {
   @HostBinding('disabled')
   public waiting = false;
    
   @Input()
   appClickWait: () => Observable<any> | Promise<any> = async() => void 0;
    
   @HostListener('click')
   clickEvent() {
      this.waiting = true;
    
      from(this.appClickWait()).pipe(take(1)).subscribe({
         subscribe: () => this.waiting = false,
         complete: () => this.waiting = false,
         error: (e) => {
            console.error(e);
            this.waiting = false;
         }
      });
   }
}
With this usage:
<button [appClickWait]="myClickFunc">Single Wait Click</button>
myClickFunc = () => this.callHttp();
This way it will only work on a button. The disabled attribute will get set automatically, and you can insert a function which returns a promise or observable.
Answered By - Poul Kruijt
 
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.