(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.64.8。结果是 5,效果很好。

我们来分解一下上面的例子,当你尝试输入1.1.

时会发生什么
  1. 键入 1 => model.rate 变为 1
  2. 输入1. => model.rate保持1
  3. 输入1.1 => model.rate保持1

最终,当您键入 1.11.2 时,模型值保持 1,因为我们 Math.round 该值。由于它没有更新 model.rate 值,因此 ngModel 绑定将忽略您进一步输入的任何内容,并显示在 input 字段中。

检查 4.64.8 完美无缺。为什么?逐步分解

  1. 键入 4 => model.rate 变为 4
  2. 输入4. => model.rate保持4
  3. 输入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));

每次我们四舍五入我们的值时都会产生一个新的对象引用。

Snippet in Stackblitz

我们也可以使用 $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 );
}