Angular 错误。 ngif 不能以一种方法工作

Angular bug. ngif not working in one method

我的程序中出现一个奇怪的错误,导致 *ngIf 停止工作。

我的方法:

stripeSubmit(){
    this.stripeCheckout = false;   // removes the form 
    this.isLoading = true;         // shows a loading spinner
       ...
        this.api.chargeCard(charge).subscribe(res =>{ 
          if(res === "Success"){
            this.api.submitOrder(this.orderObj).subscribe(res =>{
              this.isLoading = false;              // not working
              sessionStorage.clear();
              this.router.navigate(['/thank-you']) // is working but still displays /cart on top
            });
          }else {
            console.log("ELSE condition"); // works
            alert(res);                    // works
            this.stripeFailBoolean = true; // doesn't work
            this.stripeCheckout = true;    // doesn't work
            this.isLoading = false;        // doesn't work
            console.log(this.isLoading);   // logs "false"
          }
        })
      }
    });
 }

HTML:

...
    <h1 class="title">Your Cart</h1>
    <img *ngIf="isLoading"id="loading" src="../../../assets/infinitySpinner.svg" alt="">
 ...
<div *ngIf="stripeFailBoolean" class="alert alert-danger"> Something went wrong, please try again</div>

    <div *ngIf="stripeCheckout" class="stripe">
        <form class="form mt-4">
            <mat-form-field>
                <mat-label>Credit Card</mat-label>
                <input type="text" matInput [(ngModel)]="cardNumber" name="cardNumber">
            </mat-form-field>
            <br>
            <mat-form-field>
                <mat-label>Expiration Month</mat-label>
                <input type="number" matInput [(ngModel)]="expMonth" name="expMonth">
            </mat-form-field>
            <br>
            <mat-form-field>
                <mat-label>Expiration Year</mat-label>
                <input type="number" matInput [(ngModel)]="expYear" name="expYear">
            </mat-form-field>
            <br>
            <mat-form-field>
                <mat-label>CVC</mat-label>
                <input type="number" matInput [(ngModel)]="cvc" name="cvc">
            </mat-form-field>
            <br>
           
            <button class="btn btn-secondary submit" (click)="stripeSubmit()" >Submit</button>
          </form>
    </div>

正如上面注释掉的部分中所标记的那样,当我单击“提交”时,表单消失并且微调器启动。如果 chargeCard 方法成功,h1 和加载微调器将保留,“/thank-you”的内容显示在它们下面(URL 显示“/thank-you”)。

如果该方法未通过 if 语句,则警报和日志工作,但 3 个布尔值中的 none 工作并且微调器无限期地运行。

我尝试将我所有的布尔值都放在 ngDoCheck 中,但这根本没有帮助。

有谁知道为什么我的布尔值只能在这个方法中工作? 谢谢!

*****编辑: 如果您愿意,可以自己复制错误:

www.howlingwolfe.com/rentals

在底部租一条船,然后去购物车

使用信用卡成功交易: 4242 4242 4242 4242 12/2024 123

资金不足请使用信用卡: 4000 0000 0000 9995 12/2024 123

密码在www.github.com/andrethetallguy/howlingwolfefe

***********编辑 2

这是 Stackblitz 上的代码:https://angular-ivy-gyhsya.stackblitz.io

是购物车组件给我带来了麻烦

Stripe is a 3ed party tool and seems to not have any integration with Angular (judging on this line in Your repo when You are casting window to any before getting access to Stripe object) I would say this unexpected behavior is caused by running code outside of NgZone.

用非常简单的术语来说 - 如果您的代码正在调用某些代码作为某些外部代码的回调,Angular 可能不知道该代码是 运行。这意味着 Angular 不知道某些值已更改 -> 它不会触发更改检测 -> 它不会正确刷新页面。

为什么导航正常?因为 createToken 的回调中的代码导航到 https://www.howlingwolfe.com/thank-you。随着 URL 的变化,匹配的组件被使用。 Angular 不会更新视图,但会在 URL 更改时加载新组件。

为什么 console.log(this.isLoading); 有效?担心更新变量值不是 Angular 的一部分。 Angular 不知道您更改了值,但在代码中您肯定更改了它,因此 console.log 将打印正确的值。它将被更改,但如果在区域外完成,Angular 将不会对此做任何事情。

现在如何解决:将NgZone注入构造函数并包装整个代码(因此从文档中提供的第109) in this._ngZone.run(). You can follow the example行开始。

其他问题可能是您正在订阅。我这里没有文档来支持我。主要原因是取消订阅中的内部订阅变得很棘手(公平地说,您应该至少对每个订阅使用 takeUntil(unsubscribe$) (所以 ...pipe(takeUntil(this.unsubscribe$)).subscribe(...)) .

下一步将是没有内部订阅 - 这就是更高的可观察性发挥作用的时候。

举个例子,你应该避免这样做:

this.api.chargeCard(charge).subscribe(res1 => { 
    if (res1 === "Success") {
        this.api.submitOrder(this.orderObj).subscribe(res2 => {...})
    }
})

然后这样做:

this.api.chargeCard(charge).pipe(
    switchMap((res1: string) => res1 === "Success" 
        ? this.api.submitOrder(this.orderObj).pipe(tap(/* do Your code here, clear and navigate*/)) 
        : of(res1).pipe(tap(/* do Your code here on failure))),
    finalize(() => this.isLoading = false),
    takeUntil(this.unsubscribe$)
).subscribe()

switchMap tap

非常感谢@Eatos! NgZone 绝对是最佳选择。这是新的工作方法:

stripeSubmit(){
    this.stripeCheckout = false;
    this.isLoading = true;
    this.stripeFailBoolean = false;

    (<any>window).Stripe.card.createToken({
      number: this.cardNumber,
      exp_month: this.expMonth,
      exp_year: this.expYear,
      cvc: this.cvc
    }, (status: number, response: any) => {
      this.ngZone.run(() => {
      if (status === 200) {
        let charge: Charge = {
          token: response.id,
          price: this.total
        }
        // this.chargeCard(token);
        this.api.chargeCard(charge).pipe(takeUntil(this.unsubscibe), tap(()=>{
          console.log('inside of charge card tap');
          
          
        }), switchMap((res: any)=>{
          if(res === "Success"){
            return this.api.submitOrder(this.orderObj).pipe(tap(()=>{
              this.isLoading = false;
              sessionStorage.clear();
              this.router.navigate(['/thank-you'])              
            }))
          } else {
            return of(res).pipe(tap(()=>{
            this.stripeFailText = res;
            this.stripeFailBoolean = true;
            this.stripeCheckout = true;
            this.isLoading = false;
            }))
          }
          
        })).subscribe();
      }else {
        console.log(response.error.message);
        
      }
      console.log('ngZone leave');
    })
  }); 
  }   
 
  ngOnDestroy(){
    this.unsubscibe.next();
    this.unsubscibe.complete();
  }