Rahul Chandak
Rahul Chandak

Reputation: 33

Angular input box value not updated from ngModelChange event when value is cleared by Ctrl+A and prev value is same as suppose to be new value

I am using simple ngModelChange event on input box where updated value is passed on event and updated back after some operations.

<input placeholder="" #TestInput="ngModel"
        name="TestInput" 
        [(ngModel)]="testInput" 
        (ngModelChange)="onTestInputChange($event)" >

and in component

  testInput: string;
  onTestInputChange(event) {
    this.testInput =  this.testInput === ''
      ? 'cat' 
      : this.testInput==='tiger'
        ? this.testInput 
        : 'lion';
  }

Steps:

  1. When input is anything, it will updated to 'lion';
  2. When I select input by Ctrl+A and delete, value is updated to 'cat' for first time.
  3. As the value is already 'cat', if I perform step 2 again, it should update to 'cat' again, but it is not updating, surprisingly testInput is updated.

https://stackblitz.com/edit/angular-ivy-aqpcjx

Also tried change event also, no luck. Thanks!!

Upvotes: 0

Views: 1526

Answers (2)

Michael Beeson
Michael Beeson

Reputation: 2875

The problem is that you're using two-way binding on your ngModel, so both your ngModel and ngModelChange are interacting with the model change event. You can read more from this answer:

ngModel changes, ngModelChange is not called

Looking at that answer, you'll see they recommend removing the two-way binding. If you don't need two way binding for ngModel, make it only settable by changing the [()] to just []. But in your example this doesn't give the desired effect.

What I recommend is simply using the DOM level change event to run your event listener, so change (ngModelChange) to simply (change).

<input placeholder="" #TestInput="ngModel"
    name="TestInput" 
    [(ngModel)]="testInput" 
    (change)="onTestInputChange($event)" >

-- EDIT --

The above is not actually correct. The real issue is to do with when the view is updated – as long as ngModel believes it hasn't changed, it doesn't update the view (even though we can see for ourselves that the visible value of the input is different).

So when you set the value to '' the value of testInput becomes 'cat'. You delete again, and testInput is set to 'cat' again. NgModel believes nothing has changed so the view is not updated (even though we see the change on the input).

You can force Angular to fix this by running detectChanges before testInput is reset to 'cat'. To do this, you want to inject changeDetectorRef, like so:

import { Component, VERSION, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular ' + VERSION.major;
  testInput: string;

  constructor(private changeDetectorRef: ChangeDetectorRef)  {
  }
  //issue is when testInput blank valus is assigned as 'cat' again,
  // but not updated in input element.
  onTestInputChange(event) {
    this.changeDetectorRef.detectChanges();
    this.testInput =  this.testInput === ''
      ? 'cat' 
      : this.testInput === 'tiger'
        ? this.testInput 
        : 'lion';
  }
}

Now the view will be updated with every change to ngModel. Here's the fork:

https://stackblitz.com/edit/angular-ivy-bd2mu5?file=src%2Fapp%2Fapp.component.ts

That said, I still believe the (change) solution is better because it's more user friendly to wait for blur. Otherwise, they'll never even get the chance to type 'tiger' because it will always be switched to 'lion'.

Upvotes: 2

user13258211
user13258211

Reputation:

I recently answered a similiar question and iam still unsure as to why neither your solution nor input doesn't work.

To get the immediate result without first having to blur your input, you can reference your input element and change it's value during the input event.

<input placeholder="" #TestInput
         name="TestInput" 
        (input)="onTestInputChange(TestInput)" >

onTestInputChange(input) {
    input.value =  input.value === ''
      ? 'cat' 
      : input.value ==='tiger'
        ? input.value 
        : 'lion';
}

https://stackblitz.com/edit/angular-ivy-gbchzy?file=src/app/app.component.ts

Upvotes: 1

Related Questions