Angular 2 / PrimeNG - 表达式在检查后发生了变化。在最后一个无效的表单控件上绑定 NgModel

Posted

技术标签:

【中文标题】Angular 2 / PrimeNG - 表达式在检查后发生了变化。在最后一个无效的表单控件上绑定 NgModel【英文标题】:Angular 2 / PrimeNG - Expression has changed after it was checked. Binding NgModel on last invalid form control 【发布时间】:2017-07-28 00:56:28 【问题描述】:

我遇到了一个问题,当我的表单中的最后一个元素绑定了一个值时,出现错误“表达式在检查后已更改”。被抛出。

我会先说这是基于此处的 Angular 2 网站示例 - https://angular.io/docs/ts/latest/cookbook/dynamic-form.html#!#top

我的应用程序的工作方式是首先我在基于模型的表单组件中构建一个带有控件的动态表单。

我的表单组件 html 像这样循环模型中的问题

<form *ngIf="showForm" [formGroup]="formGroup">
    <!-- questions-->
    <div *ngIf="questions.length > 0">
        <div *ngFor="let question of questions">
            <question [question]="question" [formGroup]="formGroup"></question>
        </div>
    </div>
    <button pButton type="submit" label="Submit" icon="fa-check-circle-o" iconPos="left"
            [disabled]="!formGroup.valid" (click)="submitFinalForm()"></button>
</form>

下面是问题组件html,它使用从表单组件传入的数据通过ngSwitch显示某些类型的问题

<label [attr.for]="question.field">
     question.question 
</label>
<div [ngSwitch]="question.type">
    <!-- Radio / Checkbox -->
    <radio-checkbox-question *ngSwitchCase="1" [formGroup]="formGroup" [question]="question"></radio-checkbox-question>
</div>

最后是 radio-checkbox-question 组件

<div *ngIf="showQuestion" [formGroup]="formGroup">
    <!-- Radio -->
    <div *ngIf="model.radiocheckbox == 'radio'">
        <div *ngFor="let label of model.labels; let i = index;">
             <p-radioButton name="model.field"
                            value="i"
                            label="label"
                            formControlName="model.field"
                            [(ngModel)]="questionAnswerRadio"></p-radioButton>
        </div>
    </div>
</div>

这是实际的组件 TS

import  Component, Input, OnInit          from "@angular/core";
import  FormGroup                         from "@angular/forms";

import  RadioCheckboxQuestion             from "../Questions/radio.checkbox.question.model";

@Component(
    selector: "radio-checkbox-question",
    templateUrl: "radio.checkbox.component.html"
)
export class RadioCheckboxComponent implements OnInit 

    @Input() question: any;
    @Input() formGroup: FormGroup;

    model: RadioCheckboxQuestion = new RadioCheckboxQuestion();
    showQuestion: boolean = false;

    questionAnswerRadio: string = "";

    ngOnInit(): void 
        // question essential properties
        if (this.question.hasOwnProperty("field") && this.question["field"] &&
            this.question.hasOwnProperty("labels") && this.question["labels"]) 
            this.model.field = this.question["field"];
            this.model.labels = this.question["labels"];

            // assume always radio for debugging
            this.model.radiocheckbox = "radio";

            // set existing answer
            if (this.question.hasOwnProperty("QuestionAnswer") && this.question["QuestionAnswer"]) 
                if (this.model.radiocheckbox == "radio") 
                    this.questionAnswerRadio = this.question["QuestionAnswer"];
                
            

            this.showQuestion = true;
        
    

我还看到了许多类似以下的 SO 问题 Angular 2 dynamic forms example with ngmodel results in "expression has changed after it was checked" 基本上声明 [(ngModel)] 不应该与动态表单一起使用,但是 primeNG 文档说组件可以与模型驱动的表单一起使用,并且设置答案的唯一方法(据我所知)是 [(模型)]。我相信这里可能发生的事情是因为我将 formGroup 中的唯一问题设置为 formGroup 在更改检测之间变为有效并导致错误的值

Error in ./FormComponent class FormComponent - inline template:17:48 caused by: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'.

【问题讨论】:

你也可以创建plunker吗? 我不确定如何制作最新的 Angular 2 和 PrimeNG 版本,它们似乎已经过时了?奇怪的是,如果我在我的表单组件中导入 ChangeDetectionStrategy、ChangeDetectorRef 并使用 changeDetection: ChangeDetectionStrategy.OnPush 以及 private cd: ChangeDetectorRef 和 this.cd.markForCheck();手动更新我的表单组件,问题就不再发生了,但是在表单的完整版本中,来自 PrimeNG 的文本框输入被填充,而单选/复选框按钮则没有。 【参考方案1】:

我发现让更改检测对我的多嵌套组件和 primeNG 感到满意的唯一方法是手动实施完整的更改检测。这基本上意味着在每个组件中我必须添加如下内容

import ChangeDetectorRef

constructor(private change: ChangeDetectorRef)


ngOnInit() 

    // code here that inits everything
    this.change.markForCheck();

任何低于此值的情况都会导致更改检测错误以不同且独特的方式在使用 primeNG 的组件中弹出。

【讨论】:

在我的几个案例中,keeping this answer 似乎有助于找到可行的方法。【参考方案2】:

从您的模板看来,您正在使用两个模型驱动器 (formControlName) 和模板驱动 (ngModel)。

 <p-radioButton name="model.field"
                            value="i"
                            label="label"
                            formControlName="model.field"
                            [(ngModel)]="questionAnswerRadio"></p-
   <radioButton>

请选择一种方式,然后重试。 我建议你删除 [(ngModel)]

【讨论】:

以上是关于Angular 2 / PrimeNG - 表达式在检查后发生了变化。在最后一个无效的表单控件上绑定 NgModel的主要内容,如果未能解决你的问题,请参考以下文章

这个简单的 PrimeNG Angular 2 示例究竟是如何工作的?

通过 PrimeNG 从代码 Angular 2 打开上下文菜单

PrimeNG 元素没有作用域,不能使用默认的 Angular 2 ViewEncapsulation (Emulated) 设置样式

PrimeNg TabMenu Angular 11:在右侧设置最后一个选项卡

angular-安装primeng

Angular2 添加 PrimeNG 组件