我有一个输入字段,用户可以在其中输入某物的比率。
当用户输入一个值时,我希望在四舍五入后显示该值,然后将更新后的值存储在 ts
文件中的支持模型上。
使用 Angular 管道对此效果不佳,因为单向管道和更新的值不会反射(reflect)在模型上。
为了使其双向,我正在执行以下操作:
<input type="text" [ngModel]="model.rate" (ngModelChange)="model.rate=roundRate($event)" name="rate" />
roundDate
函数如下所示
roundRate(value) {
return Math.round(value);
}
现在,当我输入 5.6、7.8、9.5 等值时,它们都会四舍五入、显示并分别存储在模型上为 6、8 和 10,这是预期的行为。
当我输入 2.1、3.4、5.3 等时,问题就开始了。在这种情况下,roundRate
函数被调用,并返回四舍五入后的值。但屏幕上显示的值仍然是旧值(2.1、3.4、5.3)
我检查了 Chrome 上的 input
元素,发现 ng-reflect-model
属性已更新为预期值 (2, 3, 5)。
<input _ngcontent-c39="" name="rate" type="text" ng-reflect-name="rate" ng-reflect-model="5" class="ng-valid ng-dirty ng-touched">
有人可以解释一下这里发生了什么吗?尽管 ng-reflect-model
属性已更新,但屏幕仍然显示旧值?
感谢您的帮助!
最佳答案
首先仔细查看 ngModel
directive API ,您将看到 ngModel
是 @Input
binding,它接受一些值作为 model
变量。在初始化 ngModel 时隐式控制它 creates FormControl
。
public readonly control: FormControl = new FormControl();
更新 model
值的魔力发生在 ngOnChanges
hook 处,这样它将 View 值与 model
值同步。如果你仔细观察 ngOnChanges 钩子(Hook),你会发现它验证输入并应用其他检查,然后它严格检查 ngModel 的值是否有使用 isPropertyUpdated
方法确实发生了变化。
ngOnChanges - ngModel
指令
ngOnChanges(changes: SimpleChanges) {
this._checkForErrors();
if (!this._registered) this._setUpControl();
if ('isDisabled' in changes) {
this._updateDisabled(changes);
}
if (isPropertyUpdated(changes, this.viewModel)) {
this._updateValue(this.model); // helps to update
this.viewModel = this.model;
}
}
private _updateValue(value: any): void {
resolvedPromise.then(() => {
// set value will set form control
this.control.setValue(value, {emitViewToModelChange: false});
});
}
但是,为了实现这一点,Angular 应该在更改检测周期中识别更改。而且,由于我们没有更改模型,因此它不会触发 ngOnChanges Hook :
到目前为止我解释的是API部分。让我们回到问题上来。
尝试下面的 snippet in stackblitz ,这就是我们的期望。输入值更改时,应将该值设置为 10
本身。
<input type="text"
[ngModel]="model.rate"
(ngModelChange)="model.rate = 10"
name="rate" />
不幸的是,它不会以这种方式发生,您会看到,在最初输入任何数字时,它会将输入值更改为 10
,之后您输入的任何内容都会继续附加到号码输入。啊,想知道为什么吗?
让我们回到最初的问题,
<input type="text"
[ngModel]="model.rate"
(ngModelChange)="model.rate=roundRate($event)"
name="rate" />
{{model.rate}}
ngModel
用作单向绑定(bind)。预期的行为是,分配给 model.rate
的任何值都应反射(reflect)在 input
中。让我们尝试输入 1.1,您将看到它向我们显示值 1.1
。现在尝试输入 1.2
,结果为 1
。想知道为什么吗?但 model.rate
绑定(bind)肯定会正确更新。
同样检查 4.6
和 4.8
。结果是 5,效果很完美。
让我们分解一下上面的示例,当您尝试输入 1.1
时会发生什么。
- 类型
1
=> model.rate 变为1
- 输入
1.
=> model.rate 保持1
- 类型
1.1
=> model.rate 保持1
最终,当您输入 1.1
或 1.2
时,模型值仍为 1
,因为我们 Math.round
值。由于它不会更新 model.rate
值,因此您进一步输入的任何内容都会被 ngModel
绑定(bind)忽略,并显示在 input
内> 字段。
检查 4.6
、4.8
工作正常。为什么?逐步分解
- 类型
4
=> model.rate 变为4
- 输入
4.
=> model.rate 保持4
- 类型
4.6
=> model.rate 变为5
在这里,当您在文本框中输入 4.6
时,Math.round
值将变为 5
。这是 model.rate
(ngModel) 值的更改,并会导致 input
字段值的更新。
现在再次开始阅读答案,最初解释的技术方面也应该很清楚。
<小时/>Note: While reading an answer follow the links provided to snippet, it may help you understand it more precisely.
要使其正常工作,您可以尝试在 blur
/change
上更新您的字段,其中此事件在 fields
之外的焦点上触发,例如 Sid's answer
。它之所以有效,是因为当字段聚焦时您将更新模型一次。
但是它只能工作一次。为了不断更新,我们可以这样做:
this.model.rate = new String(Math.round(value));
每次我们舍入我们的值时,这都会产生一个新的对象引用。
关于angular - (ngModelChange) 不会更新特定输入的 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53427571/