【问题标题】:(ngModelChange) does not update the UI for specific input(ngModelChange) 不会更新特定输入的 UI
【发布时间】:2019-04-24 22:30:20
【问题描述】:

我有一个输入字段,用户可以在其中输入某物的价格。

当用户输入一个值时,我希望它在四舍五入后显示,然后将更新后的值存储在 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 函数被调用并在四舍五入后返回值。但是屏幕上显示的值仍然是旧值(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 属性已更新,为什么屏幕仍显示旧值?

感谢您的帮助!

【问题讨论】:

  • 你可以用(change)代替(ngModelChange),stackoverflow.com/questions/44840735/…
  • 你的代码不是导致无限循环吗?因为您正在重新分配在处理程序中发出事件的源。或者当您重新分配以前的值时可能不会。
  • @SteveRuben,你能解释一下这有什么帮助吗?
  • @AmitChigadani,不,它不会进入无限循环。该事件仅在用户输入内容时触发。在这种情况下,新值会被分配。它等待用户更新它。
  • @Yogesh,看看我的回答是否有帮助。

标签: angular typescript angular2-ngmodel


【解决方案1】:

首先仔细查看ngModel directive API,您会发现ngModel@Input binding,它接受一些值作为model 变量。在初始化ngModel时隐式控制creates FormControl

public readonly control: FormControl = new FormControl();

更新model 值的神奇之处发生在ngOnChanges hook,这样它将视图值与model 值同步。如果您仔细查看ngOnChanges 钩子,您会发现它会验证输入并应用其他检查,然后它会使用isPropertyUpdated 方法严格检查ngModel 的值是否真的发生了变化。

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. type 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. type 4 => model.rate 变为 4
  2. 类型4. => model.rate 保持4
  3. 输入4.6 => model.rate 变成 5

在这里,当您在文本框中输入4.6 时,Math.round 的值将变为5。这是model.rate(ngModel) 值的更改,将导致input 字段值的更新。

现在重新开始阅读答案,最初解释的技术方面也应该清楚。

注意:阅读答案时,请按照提供给 sn-p 的链接,它 可以帮助您更准确地理解它。


要使其正常工作,您可以尝试更新 blur/change 上的字段,其中此事件会在 fields 之外的焦点上触发,例如 Sid's answer。它之所以有效,是因为当该字段被聚焦时,您会更新一次模型。

但是它只能工作一次。为了不断更新,我们可以这样做:

this.model.rate = new String(Math.round(value));

这将导致每次我们对我们的值进行四舍五入时产生一个新的对象引用。

Snippet in Stackblitz

【讨论】:

  • 谢谢 Suresh,很高兴知道它有帮助 ;)
  • 我想说,这里的主要原因是 Angular 更改检测机制如何在幕后工作。如果值没有改变,它不会更新属性或触发 ngOnChanges 钩子。我们可以在这里看到它stackblitz.com/edit/…
  • 另外,模糊/更改事件只会帮助一次。如果我们输入 1.2 然后点击外部输入值将变为 1,之后如果我们尝试将值从 1 更改为 1.2 并触发模糊,则该值不会更改为 1。stackblitz.com/edit/angular-ngmodelchanges-eg-jvvzrs?file=src/…
  • 但是如果我们改变像this.model.rate = new String(Math.round(value)); 这样的值,那么change/blur 事件肯定会起作用stackblitz.com/edit/angular-ngmodelchanges-eg-upzz5p?file=src/…
  • @PankajParkar 现在我知道为什么这不起作用了。 @yurzui 是的,``(change)` 事件也只工作了一次。
【解决方案2】:

为了更简洁的实现,只需使用(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 供您参考。

【讨论】:

  • 你能告诉我我的方法有什么问题吗?
  • @Yogesh,我还在分析它。如果我发现任何实质性的东西,我会告诉你的。
  • 请务必告诉我。我很好奇。
  • @SiddAjmera buy (change) 只能在触发 blur 后调用一次。
【解决方案3】:

Angular 变化检测策略有助于在 UI 上反映变化。

请使用以下代码:

第 1 步:在您的组件中导入 ChangeDetectorRef

import { ChangeDetectorRef} from angular/core';

第 2 步:在构造函数上创建 ChangeDetectorRef 的实例。

constructor(private ref: ChangeDetectorRef){
}

第 3 步:调用要更新值的位置。

this.ref.detectChanges();

【讨论】:

  • 谢谢,我已经调用了 detectChanges() 方法,它对我有用!
  • @ZakiMohammed 你在哪里调用了这个方法?
【解决方案4】:

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

组件.ts

enterDirectRowNumberResponse(data,event){
        console.log ("Inside enterDirectRowNumberResponse",event)
        console.log ("data = ", data );
}

【讨论】:

    猜你喜欢
    • 2021-08-04
    • 1970-01-01
    • 2020-03-31
    • 1970-01-01
    • 2013-04-08
    • 1970-01-01
    • 1970-01-01
    • 2017-05-26
    • 1970-01-01
    相关资源
    最近更新 更多