Angular 2 模态弹出错误“检查后表达式已更改”

Posted

技术标签:

【中文标题】Angular 2 模态弹出错误“检查后表达式已更改”【英文标题】:Angular 2 Modal Popup Error "Expression has changed after it was checked" 【发布时间】:2017-07-08 11:37:20 【问题描述】:

Youtube video demonstrating the problem

Github repository for the demo app

我有一个非常简单的应用程序,它有一个应用程序组件、一个子组件(帐户)、处理消息对话框组件(弹出模式)的警报服务。

为了演示目的,我有两个相同的表单,一个在 app.component.ts 中,一个在 account.component.ts 中。它们每个都有一个按钮,用于调用警报服务以显示消息对话框模式。

问题是当我单击子组件(account.component.ts)的表单输入字段并“在我的键盘上按回车键”时,我收到此错误

例外:./AccountComponent 类 AccountComponent 中的错误 - 内联模板:2:2 原因:检查后表达式已更改。以前的值:“真”。当前值:“假”。 请注意,此错误不会在以下提到的任何其他情况下发生

    如果我单击按钮而不是按键盘上的 Enter 键

    来自 app.componen.ts 的表单似乎没有任何问题,即使我按 Enter 键。它似乎只是子组件(account.component.ts)。

    如果我点击account.component的输入,输入一些东西,点击按钮,没有错误显示,删除输入,回车,现在相比之前没有错误显示

我研究了 SO 和 google,似乎人们遇到了同样的问题,并通过调用更改检测来解决它。但是,我已经尝试过,并将其放在模态显示之后的地方,但它不起作用。此外,如果这样可以解决它,那么它不能解释为什么 app.component.ts 中的表单不会导致我出现这个错误。

下面是一些sn-ps的代码,完整的demo项目可以在上面的github链接上找到。这个问题困扰了我好几天。非常感谢您的帮助。

app.component.html

<label>This form is from app.component.html</label>
<form name="form" [formGroup]="changePasswordForm" (ngSubmit)="onUpdatePassword()">
    <input placeholder="Old Password" formControlName="oldPassword">
    <button class="btn btn-success">Update Password</button>
</form>

<br><br><br><br>

<label>This form is from account.component.html</label>
<router-outlet> </router-outlet>

<template ngbModalContainer></template>

app.component.ts

export class AppComponent implements OnInit 

    private changePasswordForm: FormGroup;

    constructor(
      private formBuilder: FormBuilder,
      private alertService: AlertService,
    )  

    ngOnInit() 
      this.changePasswordForm = this.formBuilder.group(
        oldPassword: [''],
      )
    

    onUpdatePassword() 
      this.alertService.alertPopup('test2', 'asfafa')
    

account.component.html

<form name="form" [formGroup]="changePasswordForm" (ngSubmit)="onUpdatePassword()">
  <input placeholder="Old Password" formControlName="oldPassword">
  <button class="btn btn-success">Update Password</button>
</form>

account.component.ts

导出类 AccountComponent 实现 OnInit

  private changePasswordForm: FormGroup;

  constructor(
    private formBuilder: FormBuilder,
    private alertService: AlertService,
  )  

  ngOnInit() 
    this.changePasswordForm = this.formBuilder.group(
      oldPassword: [''],
    )
  

  onUpdatePassword() 
    this.alertService.alertPopup('test2', 'asfafa')
  

alert.service.ts

@Injectable()
export class AlertService 
    private subject = new Subject<any>();
    private keepAfterNavigationChange = false;

    constructor(
        private router: Router,
        private modalService: NgbModal,
    )  


    alertPopup(title: string, content: string) 
        // open modal to check if worked over night
        const modalRef = this.modalService.open(MessageDialogComponent);

        modalRef.componentInstance.titleText = title
        modalRef.componentInstance.bodyText = content

        modalRef.result
            .then(response => 
            )
            .catch(() => 
                return
            )
    

message-dialog.component.html

<div class="modal-header">
  <h4 class="modal-title">titleText</h4>
</div>

<div class="modal-body">
  <p>bodyText</p>
</div>

message-dialog.component.ts

export class MessageDialogComponent implements OnInit 

  @Input() titleText: string;
  @Input() bodyText: string;

  constructor(
    public activeModal: NgbActiveModal,
  )  

  ngOnInit() 
  


【问题讨论】:

【参考方案1】:

执行以下代码后似乎您的错误发生了:

ngAfterViewInit() 
    if (!this._elRef.nativeElement.contains(document.activeElement)) 
      this._renderer.invokeElementMethod(this._elRef.nativeElement, 'focus', []);
    

https://github.com/ng-bootstrap/ng-bootstrap/blob/1.0.0-alpha.20/src/modal/modal-window.ts#L65

input 上触发blur 事件,将您的控件标记为touched

它不适用于AccountComponent,因为AccountComponent 中的检测更改发生在ngbModalContainer 之前,而app.component.html 中的FormGroup 获得正确的值。

可能的解决方案:

1) 在打开模式之前将控件标记为已触摸

account.component.ts

onUpdatePassword() 
  Object.keys(this.changePasswordForm.controls).forEach(key => 
     this.changePasswordForm.controls[key].markAsTouched();
  );

  this.alertService.alertPopup('test2', 'asfafa')

2) 更改订单标签

app.component.html

<template ngbModalContainer></template>
<router-outlet> </router-outlet>

【讨论】:

非常感谢!使用选项 2 轻松修复!请问您确实发现了问题所在,因为错误代码对于问题出在哪里没有太大意义? 老实说,调试此类错误非常困难。你需要知道变化检测是如何工作的以及angular2是如何为每个组件生成factory 但是你做的这么快?我已经被困了好几天了。谢谢! 有没有办法单步调试代码以找出导致问题的函数?好像你找到了原因的确切位置。我坐在那里想这真假是从哪里来的【参考方案2】:

我什至遇到过同样的错误,

在你的 appComponent 中使用 ngAfterViewInit() 而不是 ngOnInit。

如果还是不行就试试

setTimeOut(function()
//Your Code
,1)

【讨论】:

我的 app.component.ts ngOnInit 中唯一的代码是 this.changePasswordForm = this.formBuilder.group.... 将其放入 ngAfterViewInit 将导致 formGroup 需要一个 FormGroup 实例错误。 setTimeout 函数也是如此。 如果你的代码放在 plnkr 上会更好,好的,我会从 github 获取并检查。 非常感谢伙计。我一直在努力解决这个问题。我不知道如何在 plunker 中设置它,因为它涉及到 angular material 2 和 bootstrap 类以使模态正常工作。 嘿,伙计。非常感谢您调查它。另一个解决方案有效,所以现在一切都很好。我只是将模板更改为 router-outlet 之前

以上是关于Angular 2 模态弹出错误“检查后表达式已更改”的主要内容,如果未能解决你的问题,请参考以下文章

Angular UI Bootstrap - 模态不会在第一次点击时弹出

Angular Bootstrap 模态弹出自动对焦不起作用

使用 Angular 调用 href 打开模态

Angular4在构造函数中调用一个函数

弹出窗口的角度模态服务

VS2017 MFC应用中添加模态对话框时为其添加类弹出如下错误