(ngModelChange) не обновляет интерфейс для конкретного ввода - PullRequest
0 голосов
/ 22 ноября 2018

У меня есть поле ввода, где пользователь может ввести скорость чего-либо.

Когда пользователь вводит значение, я хочу, чтобы оно отображалось после округления, а затем обновленное значение сохранялось на вспомогательной модели в файле ts.

Использование угловоготруба не подходит для этого, так как труба в одном направлении и обновленное значение не будут отражены в модели.

Итак, чтобы сделать его двунаправленным, я делаю следующее:

<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)

Я проверил элемент input в Chrome и обнаружил, что свойство 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, почему на экране все еще отображаются старые значения?

Спасибо за вашепомощь!

Ответы [ 3 ]

0 голосов
/ 22 ноября 2018

Для более чистой реализации просто используйте свойство (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 из поля ввода


Вот Образец StackBlitz для вашей ссылки.

0 голосов
/ 22 ноября 2018

Начнем с более подробного изучения директивы ngModel API , вы увидите, что ngModel - это @Input привязка , которая принимает некоторое значение в качестве переменной model.Во время инициализации ngModel control он создает FormControl неявно.

public readonly control: FormControl = new FormControl();

Магия обновления model значения происходит от ngOnChanges hook , таким образом он синхронизирует значение представления со значением model.Если вы присмотритесь к ловушке ngOnChanges, вы обнаружите, что она проверяет ввод и применяет другие проверки, а затем строго проверяет, действительно ли значение 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:

enter image description here

То, что я до сих пор объяснял, было частью API.Вернемся к вопросу.

Попробуйте ниже фрагмент в 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,2 приведет к 1.Интересно, почему?Но, конечно, model.rate привязки обновляются правильно.Точно так же проверьте для 4.6, 4.8 результат в 5, который отлично работает.

Давайте разберем это в примере выше, что происходит, когда вы пытаетесь ввести 1.1.

  1. type 1 => model.rate становится 1
  2. type 1. => model.rate остается 1
  3. type 1.1 => model.rate остается 1

В конце концов, когда вы набираете 1.1 или 1.2 значение модели остается 1, так как мы Math.round значение.Так как значение model.rate не обновляется, все, что вы вводите дальше, просто игнорируется привязкой ngModel и отображается внутри поля input.

Проверка на 4.6, 4.8 работает отлично.Зачем?пошагово разбить его

  1. type 4 => model.rate становится 4
  2. type 4. => model.rate остается 4
  3. type 4.6 => model.rate становится 5

Здесь, когда вы вводите 4.6 в текстовое поле, Math.round значение становится 5.что означает изменение значения model.rate (ngModel), приведет к обновлению значения поля input

Теперь начните читать ответ еще раз, объясненный изначально технический аспект также будет очищен.

Примечание: читая ответ, следуйте ссылкам, предоставленным во фрагменте, это может помочь вам более точно понять его.


Чтобы это работало, попробуйте обновить поля на blur / change, где это событие запускается в фокусе из fields, как Sid's answer.Это сработало, потому что вы обновляете модель один раз, когда поле выделено.

Но работает только один раз.Чтобы постоянно обновлять информацию, мы можем сделать несколько трюков:

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

, которые будут давать новую ссылку на объект каждый раз, когда мы округляем наше значение.

Фрагмент в Stackblitz

0 голосов
/ 22 ноября 2018

В стратегии определения угловых изменений помогите отразить изменения в пользовательском интерфейсе.

Пожалуйста, используйте следующий код:

Шаг 1: Импорт ChangeDetectorRef в вашем компоненте

import { ChangeDetectorRef} from angular/core';

Шаг 2: Создать экземпляр ChangeDetectorRef в конструкторе.

constructor(private ref: ChangeDetectorRef){
}

Шаг 3: Вызовгде вы обновляете значение.

this.ref.detectChanges();
...