417

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.

Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
daniel
  • 34,281
  • 39
  • 104
  • 158

13 Answers13

717

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);
}
h0b0
  • 1,802
  • 1
  • 25
  • 44
Ramy Feteha
  • 7,577
  • 1
  • 16
  • 10
229

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.

Arjan
  • 22,808
  • 11
  • 61
  • 71
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
151

The (keyup) event is your best bet.

Let's see why:

  1. (change) like you mentioned triggers only when the input loses focus, hence is of limited use.
  2. (keypress) triggers on key presses but doesn't trigger on certain keystrokes like the backspace.
  3. (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.
  4. (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
Sagar
  • 3,107
  • 2
  • 26
  • 35
  • 6
    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. – planet_hunter Feb 20 '18 at 12:02
  • 1
    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! – Cody Mar 02 '18 at 22:56
  • is there any particular reason you would want to do this over `(ngModelChange)`? – SnailCoil Oct 05 '18 at 21:01
  • 2
    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. – Florian Leitgeb Apr 17 '19 at 15:10
  • Will this works when user copy the text from somewhere else and then paste it into our textbox? – Thariq Nugrohotomo May 08 '19 at 04:26
  • 2
    @ThariqNugrohotomo No, it would not. Use (input) if you are looking for something like that. – Sagar May 08 '19 at 14:17
  • @Sagar, I just tested this on Chrome and the keyup event fired after a paste once all keys were released. When you say this would not work, I wonder if it's browser/platform dependent? – jsaven Jan 16 '20 at 04:21
  • 1
    (keyup) helped me with backspace keystroke, thank's a lot! – theomeli May 21 '20 at 17:29
  • 1
    `keyup` as the name says only works with keys, so pasting with `Ctrl+v` is detected, while pasting with the mouse context menu is not. – Mart May 07 '21 at 09:21
40
<input type="text" [ngModel]="mymodel" (keypress)="mymodel=$event.target.value"/>
{{mymodel}}

Update

https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event

Warning: Since this event has been deprecated, you should use beforeinput or keydown instead.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 10
    works, but strangely the backspace key isn't recognized as a keypress? – daniel Feb 12 '16 at 09:53
  • 23
    You might want to use `keydown` or `keyup` instead. Some keys just don't fire on `keypress`. See also http://stackoverflow.com/questions/4843472/javascript-listener-keypress-doesnt-detect-backspace – Günter Zöchbauer Feb 12 '16 at 09:54
  • 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? – forethought Jan 05 '17 at 23:18
  • Use keypress. The input hasn't changed yet on keydown. – Günter Zöchbauer Jan 06 '17 at 06:06
  • 1
    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. – Matt Eland Apr 13 '17 at 14:32
  • The example is a bit flawed, because it doesn't make sense to bind `[ngModel]="..."` and `(keypress)="..."` to the same field (`mymodel`). It would make more sense to have something like `(keypress)="myKeyPressHandler($event.target.value)"`. I'm sure `(keypress)="$event.target.value"` alone always passes the most recent key value. If it doesn't a Plunker that allows to reproduce would be helpful. Usually `(ngModelChange)="..."` is a better option anyway. It also works better with `[ngModel]="..."` – Günter Zöchbauer Apr 13 '17 at 14:44
  • 1
    keypress is deprecated https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event – jbobbins Sep 15 '22 at 23:00
37

A different way to handle such cases is to use formControl and subscribe to its 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 value, etc.

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 {
  public searchControl: FormControl;
  public debounce: number = 400;

  ngOnInit() {
    this.searchControl = new FormControl('');
    this.searchControl.valueChanges
      .pipe(debounceTime(this.debounce), distinctUntilChanged())
      .subscribe(query => {
        console.log(query);
      });
  }
}
Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32
Salem Ouerdani
  • 7,596
  • 3
  • 40
  • 52
  • 2
    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! – hewstone Jul 02 '18 at 16:29
  • 1
    In case of multiple controls, same code can be applied with `FormGroup` – Vikash Kumar Jul 23 '19 at 09:57
23

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}}
Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
Emeka Obianom
  • 1,736
  • 3
  • 17
  • 36
8
<input type="text" (keypress)="myMethod(myInput.value)" #myInput />

archive .ts

myMethod(value:string){
...
...
}
Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
6

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);
  });
stayingcool
  • 2,324
  • 1
  • 21
  • 24
5

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.

Josef
  • 2,869
  • 2
  • 22
  • 23
Sanjiv Pradhan
  • 231
  • 4
  • 4
4

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.

Sanket Berde
  • 6,555
  • 4
  • 35
  • 39
4

In my case, the solution is:

[ngModel]="X?.Y" (ngModelChange)="X.Y=$event"
Pang
  • 9,564
  • 146
  • 81
  • 122
2

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.

1

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:['']
  });
Prabhat Maurya
  • 1,058
  • 16
  • 21