PrimeNG / p-table / p-dialog / onHide()回调导致ExpressionChangedAfterItHasBeenCheckedError

时间:2018-03-15 10:58:23

标签: angular primeng

此处提供了Plunker:http://plnkr.co/edit/vczMKlnY5yxXtzrh955m?p=preview

用例看起来很简单:

  • 包含实体p表的1个组件
  • 具有用于更新实体的p对话框的1个组件

在弹出窗口中保存单击,我想更新实体。 在弹出窗口关闭时,我想取消选择该行。

  • 当我关闭弹出窗口时(点击十字架)一切顺利。
  • 当我保存更改时,我得到ExpressionChangedAfterItHasBeenCheckedError并且该行仍然处于选中状态。
  • 当我取消更改时,我也会得到ExpressionChangedAfterItHasBeenCheckedError并且该行仍然处于选中状态。

通过在updateSuccess事件中将选择设置为null,保存时的错误消失(在plunker中TRICK 1)。

我设法找到完全防止错误的唯一解决方案是禁用通过交叉使用# Sample list of data.frame's lst <- list( data.frame(one = letters[1:10], two = 1:10), data.frame(one = letters[11:20], two = 11:20)) # Concatenate row number with entries in second column lapply(lst, function(x) { x$three <- paste(1:nrow(x), x$two, sep = "_"); x }) #[1]] # one two three #1 a 1 1_1 #2 b 2 2_2 #3 c 3 3_3 #4 d 4 4_4 #5 e 5 5_5 #6 f 6 6_6 #7 g 7 7_7 #8 h 8 8_8 #9 i 9 9_9 #10 j 10 10_10 # #[[2]] # one two three #1 k 11 1_11 #2 l 12 2_12 #3 m 13 3_13 #4 n 14 4_14 #5 o 15 5_15 #6 p 16 6_16 #7 q 17 7_17 #8 r 18 8_18 #9 s 19 9_19 #10 t 20 10_20 关闭弹出窗口的可能性,并且永远不会处理弹出窗口中的显示布尔值,而是将此责任委托给父组件(使用父级中的close事件将显示设置为false)(在plunker中TRICK 1 + TRICK 2)。

如果没有添加一些技巧来强制绑定手动刷新,我无法找到一个好的解决方案。

这里最好的解决方案是什么?

app / app.component.ts:

[closable]="false"

app / app.template.html:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: 'app/app.template.html'
})
export class AppComponent {

  public display = false;

  public selectedEntity = null;

  public cols = [
    {field: 'id', header: 'ID'},
    {field: 'prop', header: 'Property'}
  ];

  public someEntities = [
    { id: 1, prop: 'foo' },
    { id: 2, prop: 'bar' }
  ];

  public onClick() {
    this.display = true;
  }

  public onRowSelect(event) {
    this.display = true;
  }

  public onClose() {
    this.selectedEntity = null;
    // XXX TRICK 2 : Uncomment to delegate the closing responsibility to this component
    //this.display = false;
  }

  public onUpdateSuccess() {
    // XXX TRICK 1 : Uncomment to prevent the error on save
    //this.selectedEntity = null;
    // XXX TRICK 2 : Uncomment to delegate the closing responsibility to this component
    //this.display = false;
  }
}

应用程序/ popup.component.ts

<h2>PrimeNG Issue Template</h2>
<p>Please create a test case and attach the link of the plunkr to your github issue report.</p>

<p-table [columns]="cols" [value]="someEntities" (onRowSelect)="onRowSelect($event)" selectionMode="single" [(selection)]="selectedEntity">
  <ng-template pTemplate="header" let-columns>
    <tr>
      <th *ngFor="let col of columns">
          {{col.header}}
      </th>
    </tr>
  </ng-template>
  <ng-template pTemplate="body" let-rowData let-columns="columns">
    <tr [pSelectableRow]="rowData">
      <td *ngFor="let col of columns">
          {{rowData[col.field]}}
      </td>
    </tr>
  </ng-template>
</p-table>

<my-popup [(display)]="display" [myEntity]="selectedEntity" (close)="onClose()" (updateSuccess)="onUpdateSuccess($event)"></my-popup>

应用程序/ popup.component.html

import { Component, Input, Output, OnChanges, EventEmitter } from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';

@Component({
  selector: 'my-popup',
  templateUrl: 'app/popup.component.html'
})
export class MyPopupComponent implements OnChanges {
  private _display = false;
  @Input()
  get display() {
    return this._display;
  }

  set display(_display: boolean) {
    this._display = _display;
    this.displayChange.emit(_display);
  }

  @Input()
  public myEntity: any;

  @Output()
  private displayChange = new EventEmitter<boolean>();

  @Output()
  public close = new EventEmitter<void>();

  @Output()
  public updateSuccess = new EventEmitter<any>();

  public myForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.myForm = this.fb.group({
            myProp: ['', []]
        });
  }

  ngOnChanges() {
    if (this.display === true && this.myEntity) {
      this.myForm.reset({myProp: this.myEntity.prop});
    }
  }

  public onHide() {
    this.close.emit();
  }

  public onSubmit() {
    this.myEntity.prop = this.myForm.value.myProp;
    // Call to REST API to update the entity then emit the success
    this.updateSuccess.emit();
    // XXX TRICK 2 : Comment to delegate the closing responsibility to the parent component
    this.display = false;
  }
}

1 个答案:

答案 0 :(得分:0)

要摆脱ExpressionChangedAfterItHasBeenCheckedError,您必须在app.component中手动触发更改检测(请参阅Expression ___ has changed after it was checked

因此,您的onClose方法变为:

public onClose() {
    this.selectedEntity = {};
    this.cdr.detectChanges();    
}

请参阅Plunker