ChatGPT解决这个技术问题 Extra ChatGPT

Angular 2 change event on every keypress

The change event is only called after the focus of the input has changed. How can I make it so that the event fires on every keypress?

<input type="text" [(ngModel)]="mymodel" (change)="valuechange($event)" />
{{mymodel}}

The second binding changes on every keypress btw.


h
h0b0

I just used the event input and it worked fine as follows:

in .html file :

<input type="text" class="form-control" (input)="onSearchChange($event.target.value)">

in .ts file :

onSearchChange(searchValue: string): void {  
  console.log(searchValue);
}

how do we delay for some time?
The (input) event is not supported in Edge and IE browsers. What is the alternative for this in Edge browser?
Thanks, perfect for Angular 6.
spent a lot of time on change detection and what not. In the end, found peace. Thank you for this! PS: My problem was related to angular [formGroup], and I was using (change) before finding this post. Didn't know that (input) will work.
A
Arjan

Use ngModelChange by breaking up the [(x)] syntax into its two pieces, i.e., property databinding and event binding:

<input type="text" [ngModel]="mymodel" (ngModelChange)="valuechange($event)" />
{{mymodel}}
valuechange(newValue) {
  mymodel = newValue;
  console.log(newValue)
}

It works for the backspace key too.


But when using the banana in a box syntax, it only changes the model, but you cant hook something on the change event.
That's a great answer for best two-way data binding. Should be the accepted answer!
backspace key is not updating the model
That solution is correct. However, be aware that ngModelChange is fired BEFORE the value updates. So the 'newValue' in this example contains the model WITHOUT the key you just pressed - so you don't have the updated model there. If you want to have the most recent model value, use the (keyup) event.
@SateeshKumarAlli which version of Angular do you have? For me it does detect backspace in Angular 6.1.3
S
Sagar

The (keyup) event is your best bet.

Let's see why:

(change) like you mentioned triggers only when the input loses focus, hence is of limited use. (keypress) triggers on key presses but doesn't trigger on certain keystrokes like the backspace. (keydown) triggers every time a key is pushed down. Hence always lags by 1 character; as it gets the element state before the keystroke was registered. (keyup) is your best bet as it triggers every time a key push event has completed, hence this also includes the most recent character.

So (keyup) is the safest to go with because it...

registers an event on every keystroke unlike (change) event

includes the keys that (keypress) ignores

has no lag unlike the (keydown) event


As mentioned in one of the answers, input event works really well. keyup is for sure one of the safest options however, input event is one step ahead of keyup. keyup unlike input, doesn't work if the value of textbox is changed by any other way e.g. binding.
Sagar, thank you very much. This should be the Accepted Answer as it is the only one to actually address (change) alongside a workaround and then some. The accepted answer totally sucks!
This should be the accepted and safest answer as it is native and gives you full control and it is very expressive, because everyone that reads it knows exactly when the event is fired.
@ThariqNugrohotomo No, it would not. Use (input) if you are looking for something like that.
(keyup) helped me with backspace keystroke, thank's a lot!
F
Frederik Struck-Schøning
<input type="text" [ngModel]="mymodel" (keypress)="mymodel=$event.target.value"/>
{{mymodel}}

works, but strangely the backspace key isn't recognized as a keypress?
You might want to use keydown or keyup instead. Some keys just don't fire on keypress. See also stackoverflow.com/questions/4843472/…
I have tried keydown, keyup and change events but when the input is w the event handler reports to me empty string; when the input is we the event handler reports to me w as the input. Can you please explain why this behavior?
Use keypress. The input hasn't changed yet on keydown.
It's strange, but I'm getting an input lag on this where the value doesn't contain the typed value until the next keypress.
S
Salem Ouerdani

A different way to handle such cases is to use formControl and subscribe to it's valueChanges when your component is initialized, which will allow you to use rxjs operators for advanced requirements like performing http requests, apply a debounce until user finish writing a sentence, take last value and omit previous, ...

import {Component, OnInit} from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

@Component({
  selector: 'some-selector',
  template: `
    <input type="text" [formControl]="searchControl" placeholder="search">
  `
})
export class SomeComponent implements OnInit {
  private searchControl: FormControl;
  private debounce: number = 400;

  ngOnInit() {
    this.searchControl = new FormControl('');
    this.searchControl.valueChanges
      .pipe(debounceTime(this.debounce), distinctUntilChanged())
      .subscribe(query => {
        console.log(query);
      });
  }
}

This is perfect. This keeps the chatty noise down when using this for async http requests. Also already sets up the change event listeners. Brilliant! Thanks!
In case of multiple controls, same code can be applied with FormGroup
F
Frederik Struck-Schøning

The secret event that keeps angular ngModel synchronous is the event call input. Hence the best answer to your question should be:

<input type="text" [(ngModel)]="mymodel" (input)="valuechange($event)" />
{{mymodel}}

It does for me @AndreiDiaconescu
The (input) event is not supported in Edge and IE browsers. What is the alternative for this in Edge browser?
F
Frederik Struck-Schøning
<input type="text" (keypress)="myMethod(myInput.value)" #myInput />

archive .ts

myMethod(value:string){
...
...
}

Welcome to SO Ulric, please explain how your code solves the problem.
s
stayingcool

For Reactive Forms, you can subscribe to the changes made to all fields or just a particular field.

Get all changes of a FormGroup:

this.orderForm.valueChanges.subscribe(value => {
    console.dir(value);
});

Get the change of a specific field:

this.orderForm.get('orderPriority').valueChanges.subscribe(value => {
    console.log(value);
  });

Note that it will only be triggered on blur
S
Sanket Berde

What you're looking for is

<input type="text" [(ngModel)]="mymodel" (keyup)="valuechange()" />
{{mymodel}}

Then do whatever you want with the data by accessing the bound this.mymodel in your .ts file.


J
Josef

I managed to get this solved in Angular 11 by using the below code:

<input type="number" min="0" max="50" [value]="input.to" name="to"
        (input)="input.to=$event.target.value; experienceToAndFrom()">

And, the experienceToAndFrom() is a method in my component.

PS: I tried all the above solutions, but didn't work.


P
Pang

In my case, the solution is:

[ngModel]="X?.Y" (ngModelChange)="X.Y=$event"

u
user9273992

I've been using keyup on a number field, but today I noticed in chrome the input has up/down buttons to increase/decrease the value which aren't recognized by keyup.

My solution is to use keyup and change together:

(keyup)="unitsChanged[i] = true" (change)="unitsChanged[i] = true"

Initial tests indicate this works fine, will post back if any bugs found after further testing.


P
Prabhat Maurya

This question has been answered with multiple ways. However, if you would like to look at another way, specific to adding some delay before you take any action on change event then you can use the debounceTime() method with angular form valuechanges(). This code need to be added in ngOnInit() hook or create a seprate method and call it from ngOnInit().

  ngOnInit(): void {
    this.formNameInputChange();
  }

  formNameInputChange(){
    const name = this.homeForm.get('name'); // Form Control Name
    name?.valueChanges.pipe(debounceTime(1000)).subscribe(value => {
      alert(value);
    });
  }
  // this is reactive way..
  homeForm = this.fb.group({
    name:['']
  });