如何在 Ionic 4 中使用离子输入修复 *ngFor 的 ExpressionChangedAfterItHasBeenCheckedError
How to fix ExpressionChangedAfterItHasBeenCheckedError for *ngFor with ion-input in Ionic 4
我目前有一个通过 *ngFor 的离子输入,所以它会重复多次。当我想向 *ngFor 循环添加一条新记录(添加一个新的离子输入行)时,出现以下错误:
ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。先前值:'model: abc'。当前值:'model: '.
这是我的代码:
.html
<ion-item *ngFor="let item of values; let i = index;">
<ion-input formControlName="taskDetail" [(ngModel)] = "values[i].answer" type="text"></ion-input>
<ion-icon name="remove-circle" (click)="removeTaskOption(i)" slot="end"></ion-icon>
</ion-item>
.ts
checklistCounter = [];
values = [];
constructor() {
this.checklistCounter = Array(this.numCounter).fill(1).map((x, i) => i + 1);
for (let i = 0; i < this.checklistCounter.length; i++) {
let data = {
id: i,
answer:''
}
this.values.push(data);
}
}
addNewTaskOption(){
let data = {
id: (this.values[this.values.length - 1].id + 1),
answer:''
}
this.values.push(data);
}
removeTaskOption(i:number){
this.values.splice(i, 1);
}
这里有一些图片供您参考:
原图UI(在点击添加或删除离子输入行之前):
UI 的图像(在第一个离子输入中输入 'abc' 然后单击添加按钮:
错误图片:
我想要实现的是,即使用户在离子输入字段中输入了一些内容,如果他们想添加更多字段,这些字段也会出现在原始字段下方。我读过其他解决方案,但他们都订阅了一项服务,而解决方案是为此目的,所以我认为我不能将它应用到我的案例中。请帮忙!
更新:按照@GaurangDhorda 的回答设法解决了问题。
代码:
.html
<ion-item *ngFor="let item of values; let i = index;">
<ion-input formControlName="taskDetail" [value]="item.answer" (change)="setQuantity($event.target.value, i)" type="text"></ion-input>
<ion-icon name="remove-circle" (click)="removeTaskOption(i)" slot="end"></ion-icon>
</ion-item>
.ts(在新方法中添加)
setQuantity(ev:string, i:number){
this.values[i].answer = ev;
}
Add thid piece of code to your compoent.ts file...
import { Component, Input, ChangeDetectionStrategy,ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'component',
templateUrl: 'component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
constructor(private cdRef:ChangeDetectorRef){}
ngAfterViewInit() {
this.cdRef.detectChanges();
}
生产模式下不会出现以下错误。
或
在 ngAfterViewInit() 生命周期模式中添加您的值代码。
其他人警告:
检查为 "correct" 的答案只是一种错误方法的变通方法,该方法最初将模板驱动形式 (ngModel
) 与反应形式混合在一起。 (formControlName
)不应将其视为解决方案。
1) 您要么使用模板驱动形式,要么使用反应式驱动形式。混合它们,不提供真正的价值,可以被视为 "code smell" 并导致错误,如您的情况。
当您想进行双向绑定时,我建议您像在问题的初稿中那样使用 [(ngModel)]
。但是请删除 formControlName=taskDetail
。 (在任何情况下,在 *ngFor
中使用相同的 taskDetail
formControlName
都是错误的)
2) 使用您现在拥有的解决方案,您实际上是在手动进行双向绑定。
3) 我有两个例子可以说明 template driven forms and reactive forms:
的问题
模板驱动
当我们使用 ngModel
时,我们需要在您的 input
上有一个独特的 name
属性
<form>
<ion-item *ngFor="let item of values; let i = index;">
<ion-input [(ngModel)]="item.answer" type="text" name="answer_{{i}}"></ion-input>
<button (click)="removeTaskOption(i)">Remove</button>
</ion-item>
<button (click)="addNewTaskOption()">Add answer</button>
</form>
反应形式
响应式表单在组件 class 中包含更多内容。我们在那里设置了一个 FormArray
来保存值:
<form [formGroup]="myForm">
<div formArrayName="taskDetail">
<ion-item *ngFor="let item of taskDetail.controls; let i = index;">
<ion-input [formControlName]="i" type="text"></ion-input>
<button (click)="removeTaskOption(i)">Remove</button>
</ion-item>
</div>
<button (click)="addNewTaskOption()">Add answer</button>
</form>
export class AppComponent {
myForm = new FormGroup({
taskDetail: new FormArray([
new FormControl('value 1'),
new FormControl('value 2')
])
});
get taskDetail() {
return this.myForm.get('taskDetail') as FormArray;
}
addNewTaskOption(){
this.taskDetail.push(new FormControl('value NEWWW'));
}
removeTaskOption(i:number){
this.taskDetail.removeAt(i);
}
...
}
我目前有一个通过 *ngFor 的离子输入,所以它会重复多次。当我想向 *ngFor 循环添加一条新记录(添加一个新的离子输入行)时,出现以下错误:
ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。先前值:'model: abc'。当前值:'model: '.
这是我的代码:
.html
<ion-item *ngFor="let item of values; let i = index;">
<ion-input formControlName="taskDetail" [(ngModel)] = "values[i].answer" type="text"></ion-input>
<ion-icon name="remove-circle" (click)="removeTaskOption(i)" slot="end"></ion-icon>
</ion-item>
.ts
checklistCounter = [];
values = [];
constructor() {
this.checklistCounter = Array(this.numCounter).fill(1).map((x, i) => i + 1);
for (let i = 0; i < this.checklistCounter.length; i++) {
let data = {
id: i,
answer:''
}
this.values.push(data);
}
}
addNewTaskOption(){
let data = {
id: (this.values[this.values.length - 1].id + 1),
answer:''
}
this.values.push(data);
}
removeTaskOption(i:number){
this.values.splice(i, 1);
}
这里有一些图片供您参考:
原图UI(在点击添加或删除离子输入行之前):
UI 的图像(在第一个离子输入中输入 'abc' 然后单击添加按钮:
错误图片:
我想要实现的是,即使用户在离子输入字段中输入了一些内容,如果他们想添加更多字段,这些字段也会出现在原始字段下方。我读过其他解决方案,但他们都订阅了一项服务,而解决方案是为此目的,所以我认为我不能将它应用到我的案例中。请帮忙!
更新:按照@GaurangDhorda 的回答设法解决了问题。
代码:
.html
<ion-item *ngFor="let item of values; let i = index;">
<ion-input formControlName="taskDetail" [value]="item.answer" (change)="setQuantity($event.target.value, i)" type="text"></ion-input>
<ion-icon name="remove-circle" (click)="removeTaskOption(i)" slot="end"></ion-icon>
</ion-item>
.ts(在新方法中添加)
setQuantity(ev:string, i:number){
this.values[i].answer = ev;
}
Add thid piece of code to your compoent.ts file...
import { Component, Input, ChangeDetectionStrategy,ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'component',
templateUrl: 'component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
constructor(private cdRef:ChangeDetectorRef){}
ngAfterViewInit() {
this.cdRef.detectChanges();
}
生产模式下不会出现以下错误。
或
在 ngAfterViewInit() 生命周期模式中添加您的值代码。
其他人警告:
检查为 "correct" 的答案只是一种错误方法的变通方法,该方法最初将模板驱动形式 (ngModel
) 与反应形式混合在一起。 (formControlName
)不应将其视为解决方案。
1) 您要么使用模板驱动形式,要么使用反应式驱动形式。混合它们,不提供真正的价值,可以被视为 "code smell" 并导致错误,如您的情况。
当您想进行双向绑定时,我建议您像在问题的初稿中那样使用 [(ngModel)]
。但是请删除 formControlName=taskDetail
。 (在任何情况下,在 *ngFor
中使用相同的 taskDetail
formControlName
都是错误的)
2) 使用您现在拥有的解决方案,您实际上是在手动进行双向绑定。
3) 我有两个例子可以说明 template driven forms and reactive forms:
的问题模板驱动
当我们使用 ngModel
input
上有一个独特的 name
属性
<form>
<ion-item *ngFor="let item of values; let i = index;">
<ion-input [(ngModel)]="item.answer" type="text" name="answer_{{i}}"></ion-input>
<button (click)="removeTaskOption(i)">Remove</button>
</ion-item>
<button (click)="addNewTaskOption()">Add answer</button>
</form>
反应形式
响应式表单在组件 class 中包含更多内容。我们在那里设置了一个 FormArray
来保存值:
<form [formGroup]="myForm">
<div formArrayName="taskDetail">
<ion-item *ngFor="let item of taskDetail.controls; let i = index;">
<ion-input [formControlName]="i" type="text"></ion-input>
<button (click)="removeTaskOption(i)">Remove</button>
</ion-item>
</div>
<button (click)="addNewTaskOption()">Add answer</button>
</form>
export class AppComponent {
myForm = new FormGroup({
taskDetail: new FormArray([
new FormControl('value 1'),
new FormControl('value 2')
])
});
get taskDetail() {
return this.myForm.get('taskDetail') as FormArray;
}
addNewTaskOption(){
this.taskDetail.push(new FormControl('value NEWWW'));
}
removeTaskOption(i:number){
this.taskDetail.removeAt(i);
}
...
}