Angular实现类React单向表单时组件重渲染失效问题
你手动调用变更检测方法不生效,和变更检测有没有触发没有关系。
你用的是[value]="name"的单向属性绑定:当用户在输入框里打字修改内容时,只是input元素本身的DOM内部value变了,你绑定的组件属性name从头到尾没有发生变化。这时候不管你调用ChangeDetectorRef.detectChanges()还是ApplicationRef.tick(),Angular跑完脏检查都会判定「绑定的源值name没变化,不需要更新DOM」,自然不会把输入框里用户输入的内容重置回name对应的原始值。
之前用setTimeout先置空name再还原的方案之所以能生效,本质是你强制修改了两次name的值,给了Angular两次更新DOM的信号,但这种hack写法有闪烁、没法适配复杂对象的问题,完全可以用更合理的方案替代。
方案1:直接同步DOM值(适合单字段简单场景)
不需要修改组件属性、不需要强制触发变更检测,直接把输入框的DOM值重置为原始的name即可,无闪烁、逻辑简单。
第一步给模板里的input加模板引用变量:
<hello name="{{ name }}"></hello> <p> <input #nameInput type="text" [value]="name" (change)="onChange($event)" /> <button type="button" (click)="onCancel()"> Cancel </button> <button type="button" (click)="onSave()"> Save </button> </p>
第二步在组件里获取输入框引用,在取消逻辑里直接重置DOM值:
import { Component, ViewChild, ElementRef } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) export class AppComponent { name = 'John'; temporaryValue = ''; @ViewChild('nameInput') nameInput!: ElementRef<HTMLInputElement>; onChange($event: Event) { this.temporaryValue = ($event.target as HTMLInputElement).value; } onCancel() { // 直接将输入框DOM值重置为原始name this.nameInput.nativeElement.value = this.name; // 清空临时存储的输入内容 this.temporaryValue = ''; } onSave() { this.name = this.temporaryValue; } }
方案2:使用响应式表单(适合多字段复杂表单场景,官方推荐实践)
如果表单包含多个字段、字段值是复杂对象,不要自己手写临时变量存输入值,直接用Angular内置的响应式表单能力,天生支持值同步、重置,不需要手动操作DOM,也不会有闪烁问题。
首先在所属模块导入ReactiveFormsModule,之后组件代码调整为:
import { Component } from '@angular/core'; import { FormControl } from '@angular/forms'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) export class AppComponent { name = 'John'; // 用原始name值初始化表单控件 nameControl = new FormControl(this.name); onCancel() { // 调用reset方法直接重置控件值,自动同步到输入框DOM this.nameControl.reset(this.name); } onSave() { // 保存时把控件值同步到正式的name属性即可 this.name = this.nameControl.value; } }
对应模板调整为绑定表单控件:
<hello name="{{ name }}"></hello> <p> <input type="text" [formControl]="nameControl" /> <button type="button" (click)="onCancel()"> Cancel </button> <button type="button" (click)="onSave()"> Save </button> </p>
如果是多字段表单,只需要把FormControl换成FormGroup,调用reset方法时传入整个表单的初始值对象,就能一次性重置所有字段,完全不需要写额外的赋值逻辑,适配性极强。
内容的提问来源于stack exchange,提问作者IsmailS

