0

My keypress event handler is always one keystroke behind and somehow $timeout fixes this.

<body ng-controller="myController">
  <form>
    <input ng-keypress="handleKeypress($event)" />
  </form>
</body>

Controller:

myapp.controller('myController', function($scope, $timeout){    
    $scope.handleKeypress = function(ev){

      console.log(ev.target.value); //always one keystroke behind

      //$timeout(function(){console.log(ev.target.value);}); //works!         
    };
}); 

Why is $timeout necessary and why/how does it work?

I understand that with the keypress event the character hasn't yet been inserted into the DOM, but I'm confused because the Hello World example at angularjs.org responds before a key is released.

(Their example doesn't use a custom event handler, but clearly Angular is updating the model before the keyup event, so I'd like understand more about doing this in a custom event handler.)

event.which is available on keypress and keydown, and I thought perhaps Angular might be using String.fromCharCode(), but I don't see anything in the Angular source to this effect.

TheSharpieOne
  • 25,646
  • 9
  • 66
  • 78
KnewB
  • 353
  • 4
  • 16
  • $timeout forces a digest to happen. Although ng-keypress should also trigger a digest, so I'm not sure why it's one stroke behind. Is there anymore code you're not posting? – Jonathan Rowny Jan 27 '14 at 19:02
  • 1
    I think it's a problem with onkeypress event, check these: http://stackoverflow.com/questions/9349587/javascript-onkeypress-event-fires-but-input-text-value-is-incorrect , http://stackoverflow.com/questions/5340751/javascript-last-character-missing-on-onkeypress-event – Ilan Frumer Jan 27 '14 at 19:06

1 Answers1

2

$timeout allows for your function to run in the next cycle, after the value has been added to the field at the end of the current cycle. Basically, with $timeout, you are telling it to wait, and while it waits ev.target.value gets updated.

As far as the example. Angular is running dirty checks looking for things that change on UI events, that is how it knows to update its value. Its happening after the keypress event. The dirty checks are timeout based. You can see here: http://jsfiddle.net/TheSharpieOne/V6p9M/1/ that the $scope is not updated when your function runs. Change the log to an alert and you see the DOM is not updated either.

If you are trying to avoid that $timeout call you can use the ngChange event. Unlike the normal onChange (which fires on blur), ngChange will fire every time the value is changed, such as a keystroke.

http://jsfiddle.net/TheSharpieOne/V6p9M/

TheSharpieOne
  • 25,646
  • 9
  • 66
  • 78
  • I have a vague notion of the browser event loop -is that what's meant by "cycle"? – KnewB Jan 29 '14 at 17:54
  • 1
    Yes. Sorry for the difference in terminology. To me [in my mind] the event loop is the process and cycle is one instance/run-through of the event loop. – TheSharpieOne Jan 29 '14 at 18:15