等待点击 Promise 完成的 Angular 指令

Posted

技术标签:

【中文标题】等待点击 Promise 完成的 Angular 指令【英文标题】:Angular directive that waits for click Promise to complete 【发布时间】:2019-07-08 15:09:56 【问题描述】:

更新 1:根据@PierreDuc 的回答,我在这里分叉并创建了一个更简单的版本:Simplified Example。伙计,我记得在 AngularJS 中能够完全劫持(点击)函数并执行它。我明白了,我明白了,不过我喜欢下面的简化提案。那应该对我有用:)

我创建了一个 Angular 按钮指令,它接受一个方法作为 Promise。 单击时,我禁用该按钮。当 Promise 解决后,我会重新启用该按钮。

基本上,在单击时,我想禁用该按钮,直到该按钮事件正在执行的所有处理完成,包括任何 http 调用。

我已经完成了我在这里看到的目标:Stackblitz Example。

我不是特别喜欢我的“解决方案”。

这太复杂了。为了让它发挥作用,需要发生三件事。

    指令被添加到按钮。 设置“waitFor”属性。 “waitFor”函数必须是返回 Promise 的函数。

IMO,需要调整的东西太多了。

我真正想做的是获取按钮的 (click) 方法的句柄,并在指令中手动执行它,就像我执行“waitFor”属性的方式一样。

我该怎么做?

至少,我想要一个不需要指令名称(“appClickWait”)和属性(“[waitFor]”)的指令。

为方便起见,这里是代码:

指令:

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();
      );
  

模板:

  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>

谢谢!

【问题讨论】:

【参考方案1】:

我想您可以将其简化为:

@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;
      
    )
  

这种用法:

<button [appClickWait]="myClickFunc">Single Wait Click</button>

myClickFunc = () => this.callHttp();

这样它只能在一个按钮上工作。 disabled 属性将自动设置,您可以插入一个返回 promise 或 observable 的函数。

stack;

【讨论】:

非常感谢您的回复!这确实是一个更简单的用法。我也从你的例子中学到了一些东西,所以 TYVM。我应该对此感到高兴,但是,作为开发人员,我想让它变得更简单,哈哈 :) 工作示例:stackblitz.com/edit/… @user1949561 我已经稍微简化了你的堆栈。链接在答案的底部。我不知道它应该变得更简单:)。您从该指令收到的每个请求都是一行。另外建议,在 99.9& 的情况下,您不应该使用渲染器。它的棱角分明。 爱它,再次非常感谢!啊,'禁用',我喜欢想象我会迭代直到我意识到:P

以上是关于等待点击 Promise 完成的 Angular 指令的主要内容,如果未能解决你的问题,请参考以下文章

Promise.all 的 then() 函数在 Promise 完成之前执行 - Ionic/Angular

Promise.all() 不等待异步进程

Javascript等待Promise在返回响应之前完成for循环

TypeScript - 等待 observable/promise 完成,然后返回 observable

在返回函数的变量之前,如何等待 promise 完成?

在得到我的 Observable 之前等待一个 Promise