web-dev-qa-db-fra.com

(ngModelChange) ne met pas à jour l'interface utilisateur pour une entrée spécifique

J'ai un champ de saisie où l'utilisateur peut entrer le taux de quelque chose.

Lorsque l'utilisateur entre une valeur, je souhaite qu'elle s'affiche après l'arrondi, puis la valeur mise à jour soit stockée sur le modèle de support dans le fichier ts.

Utiliser un tube Angular angulaire n'est pas bon pour cela car un tube dans une direction et la valeur mise à jour ne seront pas reflétés sur le modèle.

Donc, pour le rendre bidirectionnel, je fais ce qui suit:

<input type="text" [ngModel]="model.rate" (ngModelChange)="model.rate=roundRate($event)" name="rate" />

La fonction roundDate ressemble à ceci

roundRate(value) {
    return Math.round(value);
}

Maintenant, lorsque j'entre des valeurs telles que 5,6, 7,8, 9,5, etc., elles sont toutes arrondies, affichées et stockées sur le modèle à 6, 8 et 10 respectivement, ce qui est le comportement attendu.

Le problème commence lorsque j'entre 2.1, 3.4, 5.3 etc. Dans ce cas, la fonction roundRate est appelée et renvoie la valeur après l'arrondi. Mais les valeurs affichées à l'écran sont toujours les anciennes valeurs (2.1, 3.4, 5.3)

J'ai inspecté l'élément input sur Chrome et j'ai constaté que la propriété ng-reflect-model Était mise à jour aux valeurs attendues (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">

Quelqu'un peut-il expliquer ce qui se passe ici et malgré la mise à jour de la propriété ng-reflect-model, Pourquoi l'écran affiche toujours les anciennes valeurs?

Merci de votre aide!

4
Yogesh

Pour une implémentation plus propre, utilisez simplement la propriété (change)@Output Et [(ngModel)]. L'implémentation de roundRate changera quelque chose comme ceci:

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

Et dans le modèle:

<input type="text" [(ngModel)]="model.rate" (change)="roundRate()" name="rate" />

PS: Cela ne mettra à jour la valeur qu'une fois que vous blur à partir de votre champ de saisie


Voici un exemple StackBlitz pour votre référence.

2
SiddAjmera

Commencez par regarder de plus près la directive ngModel API , vous verrez que ngModel est @Input Contraignant qui accepte une valeur comme variable model. Au moment de l'initialisation ngModel contrôlez-le crée FormControl implicitement.

public readonly control: FormControl = new FormControl();

La magie de la mise à jour de la valeur model se produit à partir de ngOnChanges hook , de cette façon, il synchronise la valeur de la vue avec la valeur model. Si vous regardez de plus près le crochet ngOnChanges, vous constaterez que cela valide l'entrée et applique également d'autres vérifications, après quoi il vérifie strictement que si la valeur de ngModel la valeur est vraiment modifiée à l'aide de isPropertyUpdated méthode.

ngOnChanges - Directive 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}); 
    });
}

Mais pour y arriver Angular devrait reconnaître les changements pendant le cycle de détection des changements. Et puisque nous n'avons pas changé notre modèle, il ne déclenchera pas le hook ngOnChanges:

enter image description here

Ce que j'ai expliqué jusqu'à présent, c'était la partie API. Revenons à la question.

Essayez ci-dessous extrait de stackblitz , quelle serait notre attente. Lors d'un changement de valeur d'entrée, il doit définir cette valeur sur 10 Lui-même.

<input type="text" 
  [ngModel]="model.rate" 
  (ngModelChange)="model.rate = 10"
  name="rate" />

Malheureusement, cela ne se produit pas de cette façon, vous verrez que lors de la frappe initiale d'un nombre, cela changera la valeur d'entrée en 10 Et plus tard, tout ce que vous saisirez continuera à s'ajouter à la saisie de numéro. Ahh, je me demande pourquoi?

Revenons à la question d'origine,

<input type="text" 
  [ngModel]="model.rate" 
  (ngModelChange)="model.rate=roundRate($event)"
  name="rate" />
 {{model.rate}}

ngModel utilisé comme liaison unidirectionnelle. Le comportement attendu est que toutes les valeurs affectées au model.rate Doivent être reflétées à l'intérieur du input. Essayons d'entrer 1.1, vous verrez qu'il nous montre la valeur 1.1. Maintenant, essayez de saisir 1.2, Les résultats en 1,2 résultent en 1. Vous vous demandez pourquoi? Mais les liaisons model.rate Sont certainement mises à jour correctement. De même, vérifiez 4.6, 4.8 Donne 5 qui fonctionne parfaitement.

Décomposons l'exemple ci-dessus, que se passe-t-il lorsque vous essayez d'entrer 1.1.

  1. tapez 1 => model.rate devient 1
  2. tapez 1. => model.rate stay 1
  3. tapez 1.1 => model.rate stay 1

Finalement, lorsque vous tapez 1.1 Ou 1.2, La valeur du modèle reste 1 Car nous Math.round Valeur. Puisqu'il ne s'agit pas d'une mise à jour de la valeur model.rate, Tout ce que vous entrez plus loin est simplement ignoré par la liaison ngModel et affiché dans le champ input.

Vérifiez 4.6, 4.8 Fonctionne parfaitement. Pourquoi? décomposer étape par étape

  1. tapez 4 => model.rate devient 4
  2. tapez 4. => model.rate stay 4
  3. tapez 4.6 => model.rate devient 5

Ici, lorsque vous entrez 4.6 Dans la zone de texte, la valeur de Math.round Devient 5. qui est une modification de la valeur de model.rate (ngModel) entraînerait une mise à jour de la valeur du champ input

Maintenant, recommencez à lire la réponse, l'aspect technique expliqué initialement sera également effacé.

Remarque: Lors de la lecture d'une réponse, suivez les liens fournis vers l'extrait de code, cela peut vous aider à la comprendre plus précisément.


Pour le faire fonctionner, vous pouvez essayer de mettre à jour vos champs sur blur/change où cet événement se déclenche sur focus hors de fields comme Sid's answer . Cela a fonctionné parce que vous mettez à jour le modèle une fois que le champ est concentré.

Mais cela ne fonctionne qu'une seule fois. Pour garder les mises à jour constamment, nous pouvons faire quelques trucs:

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

ce qui entraînera une nouvelle référence d'objet chaque fois que nous arrondirons notre valeur.

Extrait dans Stackblitz

7
Pankaj Parkar

In Angular la stratégie de détection de changement aide à refléter les changements sur l'interface utilisateur.

Veuillez utiliser le code ci-dessous:

Étape 1: Importez ChangeDetectorRef dans votre composant

import { ChangeDetectorRef} from angular/core';

Étape 2: Créer une instance de ChangeDetectorRef sur le constructeur.

constructor(private ref: ChangeDetectorRef){
}

Étape 3: Appelez où vous mettez à jour la valeur.

this.ref.detectChanges();
0
sanjay kumar

Nous pouvons également obtenir sa valeur en utilisant $ event et non en utilisant 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 );
}
0
Techdive