(ngModelChange) 不更新特定输入的 UI
(ngModelChange) does not update the UI for specific input
我有一个输入字段,用户可以在其中输入某物的速率。
当用户输入一个值时,我希望在四舍五入后显示该值,然后将更新后的值存储在 ts
文件中的支持模型中。
使用 Angular 管道对此不利,因为单向管道和更新值不会反映在模型上。
因此,为了使其成为双向的,我正在执行以下操作:
<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
函数被调用,它 returns 是四舍五入后的值。但屏幕上显示的值仍然是旧值 (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
属性 得到更新,为什么屏幕仍然显示旧值?
感谢您的帮助!
In Angular change detection strategy help to reflect changes on UI.
请使用以下代码:
第 1 步: 在您的组件中导入 ChangeDetectorRef
import { ChangeDetectorRef} from angular/core';
第 2 步: 在构造函数上创建 ChangeDetectorRef
的实例。
constructor(private ref: ChangeDetectorRef){
}
第 3 步:调用更新值的位置。
this.ref.detectChanges();
为了更简洁的实现,只需使用 (change)
@Output
属性 和 [(ngModel)]
。 roundRate
的实现将改变如下:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
model = { rate: null };
roundRate() {
this.model.rate = Math.round(+this.model.rate);
}
}
在模板中:
<input type="text" [(ngModel)]="model.rate" (change)="roundRate()" name="rate" />
PS: 这只会在您从输入字段blur
中更新值
这里有一个 Sample StackBlitz 供您参考。
开始仔细观察隐式的 ngModel
directive API, you will see that ngModel
is @Input
binding which accepts some value as model
variable. At the time of initializing ngModel
control it creates FormControl
。
public readonly control: FormControl = new FormControl();
更新 model
值的魔力发生在 ngOnChanges
hook,这样它将视图值与 model
值同步。如果仔细查看 ngOnChanges
钩子,您会发现它验证输入并应用其他检查,之后它会使用 [= 严格检查 ngModel
的值是否真的改变了26=]方法。
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 钩子:
到目前为止我解释的是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
用作单向绑定。预期的行为是,分配给 model.rate
的任何值都应反映在 input
中。让我们尝试输入 1.1,您会看到它向我们显示了值 1.1
。现在尝试输入 1.2
,结果是 1
。想知道为什么吗?但肯定 model.rate
绑定更新正确。
同样检查 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
绑定将忽略您进一步输入的任何内容,并显示在 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
的焦点,例如 。它之所以有效,是因为您在关注该字段时更新了一次模型。
但是它只能工作一次。为了保持不断更新,我们可以这样做:
this.model.rate = new String(Math.round(value));
每次我们四舍五入我们的值时都会产生一个新的对象引用。
我们也可以使用 $event 而不是使用 ngModel-
来获取它的值
component.html
<table>
<tbody>
<tr>
<td colspan="2" style="width:100px;height:38px;"><input type = "text" (change)="enterDirectRowNumberResponse(data,$event.target.value)"></td></tr>
</tbody
</table>
component.ts
enterDirectRowNumberResponse(data,event){
console.log ("Inside enterDirectRowNumberResponse",event)
console.log ("data = ", data );
}
我有一个输入字段,用户可以在其中输入某物的速率。
当用户输入一个值时,我希望在四舍五入后显示该值,然后将更新后的值存储在 ts
文件中的支持模型中。
使用 Angular 管道对此不利,因为单向管道和更新值不会反映在模型上。
因此,为了使其成为双向的,我正在执行以下操作:
<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
函数被调用,它 returns 是四舍五入后的值。但屏幕上显示的值仍然是旧值 (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
属性 得到更新,为什么屏幕仍然显示旧值?
感谢您的帮助!
In Angular change detection strategy help to reflect changes on UI.
请使用以下代码:
第 1 步: 在您的组件中导入 ChangeDetectorRef
import { ChangeDetectorRef} from angular/core';
第 2 步: 在构造函数上创建 ChangeDetectorRef
的实例。
constructor(private ref: ChangeDetectorRef){
}
第 3 步:调用更新值的位置。
this.ref.detectChanges();
为了更简洁的实现,只需使用 (change)
@Output
属性 和 [(ngModel)]
。 roundRate
的实现将改变如下:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
model = { rate: null };
roundRate() {
this.model.rate = Math.round(+this.model.rate);
}
}
在模板中:
<input type="text" [(ngModel)]="model.rate" (change)="roundRate()" name="rate" />
PS: 这只会在您从输入字段blur
这里有一个 Sample StackBlitz 供您参考。
开始仔细观察隐式的 ngModel
directive API, you will see that ngModel
is @Input
binding which accepts some value as model
variable. At the time of initializing ngModel
control it creates FormControl
。
public readonly control: FormControl = new FormControl();
更新 model
值的魔力发生在 ngOnChanges
hook,这样它将视图值与 model
值同步。如果仔细查看 ngOnChanges
钩子,您会发现它验证输入并应用其他检查,之后它会使用 [= 严格检查 ngModel
的值是否真的改变了26=]方法。
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 钩子:
到目前为止我解释的是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
用作单向绑定。预期的行为是,分配给 model.rate
的任何值都应反映在 input
中。让我们尝试输入 1.1,您会看到它向我们显示了值 1.1
。现在尝试输入 1.2
,结果是 1
。想知道为什么吗?但肯定 model.rate
绑定更新正确。
同样检查 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
绑定将忽略您进一步输入的任何内容,并显示在 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
的焦点,例如
但是它只能工作一次。为了保持不断更新,我们可以这样做:
this.model.rate = new String(Math.round(value));
每次我们四舍五入我们的值时都会产生一个新的对象引用。
我们也可以使用 $event 而不是使用 ngModel-
来获取它的值component.html
<table>
<tbody>
<tr>
<td colspan="2" style="width:100px;height:38px;"><input type = "text" (change)="enterDirectRowNumberResponse(data,$event.target.value)"></td></tr>
</tbody
</table>
component.ts
enterDirectRowNumberResponse(data,event){
console.log ("Inside enterDirectRowNumberResponse",event)
console.log ("data = ", data );
}