在我的函数 ngDoCheck() Angular 中关闭 matDialog 时出现问题

Posted

技术标签:

【中文标题】在我的函数 ngDoCheck() Angular 中关闭 matDialog 时出现问题【英文标题】:Problem closing my matDialog in my function ngDoCheck() Angular 【发布时间】:2019-09-27 02:29:30 【问题描述】:

在我的 ngDoCheck() 函数中关闭我的 matDialog Angular Material 时遇到问题。

编辑: StackBlitz HERE

说明:

在我的主页上,我有一个按钮可以打开 matDialog 以将数据提取或不提取到 Excel 文件中。 在我的matDialog 中,我有两个按钮。 一个关闭matDialog,另一个启动函数extractWallet(), 恢复数据库中的数据。

在这个函数中,我使用waitExtraction 变量禁用我的按钮,并禁用matDialog 的关闭,直到提取完成。

我的loadInvoiceExtractionXXX() 方法允许我恢复我的数据库数据,一旦收到数据, completedExtraction 设置为 [false, false, false] 在函数对应的索引(0、1 或 2)上设置为 true

在我的ngOnInit() 中,数据通过Observer 接收并保存在局部变量中。 因此在extractWallet()中执行loadInvoiceExtractionXXX()后接收到新数据。

我的问题发生在这里。我有一个ngDoCheck() 函数,用于检查何时使用this.completedExtraction=[true, true, true] 接收到所有三个数据。 我的 Excel 文件已创建并上传到浏览器。 提取现已完成,我现在想用this.dialogRef.close() 关闭我的matDialog

但显示如下错误:

错误:ExpressionChangedAfterItHasBeenCheckedError: 检查后表达式发生了变化。

上一个值:'@dialogContainer: enter'。 当前值:'@dialogContainer: exit'。

我试图在ngAfterViewChecked() 中关闭我的matDialog,但显示相同的错误。 如果有人有解决方案让我在提取后关闭我的matDialog,我会很感兴趣。

我的代码供您使用:

export class ExtractWalletComponent implements OnInit, DoCheck, OnDestroy 

  private subscription$: Subscription = new Subscription();

  selectedWallet: Wallet;
  waitExtraction: boolean;
  completedExtraction: boolean[];

  invoiceProduct: InvoiceExtractionProduct[];
  invoiceSupport: InvoiceExtractionSupport[];
  invoiceTraining: InvoiceExtractionTraining[];

  constructor(public dialogRef: MatDialogRef<ExtractWalletComponent>,
              public storeWallets: WalletsComponentStore,
              public snackBar: MatSnackBar)  

  ngOnInit(): void 
    this.waitExtraction = false;
    this.completedExtraction = [false, false, false];

    this.subscription$.add(this.storeWallets.selectedWallet$.subscribe(wallet => this.selectedWallet = wallet));
    this.subscription$.add(this.storeWallets.invoiceExtractionProduct$.subscribe(invoiceProduct => this.invoiceProduct = invoiceProduct));
    this.subscription$.add(this.storeWallets.invoiceExtractionSupport$.subscribe(invoiceSupport => this.invoiceSupport = invoiceSupport));
    this.subscription$.add(this.storeWallets.invoiceExtractionTraining$.subscribe(invoiceTraining => this.invoiceTraining = invoiceTraining));
  

  ngDoCheck(): void 
    if(this.completedExtraction[0] == true && this.completedExtraction[1] == true && this.completedExtraction[2] == true) 
      this.completedExtraction[0] = false;
      this.completedExtraction[1] = false;
      this.completedExtraction[2] = false;
      this.dialogRef.disableClose = false;

      const wb: XLSX.WorkBook = XLSX.utils.book_new();
      const ws1: XLSX.WorkSheet = XLSX.utils.json_to_sheet(this.invoiceProduct);
      const ws2: XLSX.WorkSheet = XLSX.utils.json_to_sheet(this.invoiceSupport);
      const ws3: XLSX.WorkSheet = XLSX.utils.json_to_sheet(this.invoiceTraining);

      XLSX.writeFile(wb, this.selectedWallet.user.num_seller + '_' + this.selectedWallet.period.month +
                          '_' + this.selectedWallet.period.year + '_' + this.selectedWallet.id + '.xlsx');

      this.dialogRef.close(); //<-- ERROR
    
  

  ngOnDestroy(): void 
    this.subscription$.unsubscribe();
    this.storeWallets.clearExtraction();
  

  extractWallet(): void 
    this.waitExtraction = true;
    this.dialogRef.disableClose = true;
    this.snackBar.open('Extraction en cours', 'OK',  duration: 5000 );

    this.storeWallets.loadInvoiceExtractionProduct(this.selectedWallet).subscribe(
      res => 
        if(res)  this.completedExtraction[0] = true;
      ,
      err =>  console.error(err); this.dialogRef.close(); this.snackBar.open('Echec de l\'extraction', 'OK',  duration: 2000 ); 
    );

    this.storeWallets.loadInvoiceExtractionSupport(this.selectedWallet).subscribe(
      res => 
        if(res)  this.completedExtraction[1] = true; 
      ,
      err =>  console.error(err); this.dialogRef.close(); this.snackBar.open('Echec de l\'extraction', 'OK',  duration: 2000 ); 
    );

    this.storeWallets.loadInvoiceExtractionTraining(this.selectedWallet).subscribe(
      res => 
        if(res)  this.completedExtraction[2] = true; 
      ,
      err =>  console.error(err); this.dialogRef.close(); this.snackBar.open('Echec de l\'extraction', 'OK',  duration: 2000 ); 
    );
  

提前感谢您的帮助。

PS:对不起我的英语(谷歌翻译)。

【问题讨论】:

【参考方案1】:

嗯,问题正是它告诉您的内容:您在更改检测周期中更改了某些内容。您需要将更改放在循环之外,例如:

ngDoCheck(): void 
   if(this.completedExtraction[0] == true && this.completedExtraction[1] == true && this.completedExtraction[2] == true) 
      // omitted

      // Run after change detection
      setTimeout(() => this.dialogRef.close());

   

但我认为主要问题是您在滥用 ngDoCheck 生命周期钩子。为什么不对 Observables 做出反应?

combineLatest([
   this.storeWallets.loadInvoiceExtractionProduct(this.selectedWallet)
      .pipe(/* Add your catchError logic for only this observable */),
   this.storeWallets.loadInvoiceExtractionSupport(this.selectedWallet),
   this.storeWallets.loadInvoiceExtractionTraining(this.selectedWallet),
]).pipe(
   filter(results => results.every(res => !!res))
).subscribe(results => 
   // Called when all 3 returned positively
)

【讨论】:

感谢您的回答,它适用于setTimeout,但我会尝试使用combineLatest

以上是关于在我的函数 ngDoCheck() Angular 中关闭 matDialog 时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

ngDoCheck VS 使用函数作为属性值 - 性能差异

如何在我的应用程序中包含 angular-ui-slider? (TypeError:elm.slider 不是函数)

我无法在我的项目中使用 Angular 材料 [重复]

Angular 6调用函数从1个组件到另一个组件但数据没有改变

从 Angular 中的 JavaScript 文件调用 Typescript 函数

当我们只有一个要接收的对象时如何在Angular 8中获取数据