25

In several places of my Angular app I need to clear inputs from user with the ESC key. The problem is, I don't know how to do it with text input fields (textarea is clearing OK). See this fiddle:

jsFiddle demonstration of the problem

Binding:

<input ng-model="search.query" ui-keypress="{esc: 'keyCallback($event)'}" />

Callback I use:

$scope.keyCallback = function($event) {
  $event.preventDefault();
  $scope.search.query = '';
}

Can anyone, please, figure out what I need to do to clear text input with ESC key?

SOLUTION: As adviced by bmleite, you shouldn't listen for 'keypress' but for 'keydown' and 'keyup'. Problem was, that 'keydown' does not work in Firefox so only 'keyup' did the magic trick with listening for ESC. ;)

Working fiddle: http://jsfiddle.net/aGpNf/190/

SOLUTION UPDATE: In the end I had to listen for both 'keydown' and 'keyup' events. Because in my case FF does reset input field on ESC keydown to previous state, so it messed up my model. So 'keyup' clears the model and 'keydown' checks if model is empty and does appropriate action. I also need to manually defocus input to prevent text popping back in. :/

braX
  • 11,506
  • 5
  • 20
  • 33
Jan Peša
  • 6,760
  • 4
  • 27
  • 32
  • Can you explain what you mean by SOLUTION UPDATE? It sounds like it would help you out if AngularUI wraps your keyup in a `$scope.$apply()`? If that's the case can you open an issue? I also am curious what you mean by text popping back in? Also, you can do the following: `ui-keydown="{ 'esc enter': 'keyCallback($event)' }"` and either key will trigger the event. – ProLoser Feb 01 '13 at 19:29
  • if someone comes here and just can not get the esc to fire in Chrome ... turn off the Vimium plugin or other. – Skylar Saveland Apr 23 '13 at 07:24
  • 1
    This no longer seems to work in either Chrome or Firefox. I have not tested in IE/Edge or Opera. – Terra Ashley Jul 01 '16 at 05:31
  • https://stackoverflow.com/a/46863552/2074346 - *Best Answer* – Kishore Sahasranaman Jan 04 '21 at 15:21

6 Answers6

31

The accepted answer does not work for IE 10/11. Here is a solution based on another question that does:

Directive

.directive('escKey', function () {
  return function (scope, element, attrs) {
    element.bind('keydown keypress', function (event) {
      if(event.which === 27) { // 27 = esc key
        scope.$apply(function (){
          scope.$eval(attrs.escKey);
        });

        event.preventDefault();
      }
    });
    scope.$on('$destroy', function() {
        element.unbind('keydown keypress')
    })
  };
})

HTML:

<input ... ng-model="filter.abc" esc-key="resetFilter()" >

Ctrl

$scope.resetFilter = function() {
  $scope.filter.abc = null;
};
Basheer AL-MOMANI
  • 14,473
  • 9
  • 96
  • 92
s.Daniel
  • 1,064
  • 12
  • 29
  • 3
    [eval is evil](https://jslinterrors.com/eval-is-evil), you can bind method to directive in a proper way using `&`, more info [here](https://docs.angularjs.org/guide/directive). – icl7126 Nov 06 '15 at 23:29
  • 3
    I loved this so much that, after using it on a bunch of stuff I just went ahead and make a repo for it. https://github.com/deltreey/angular-esc-key If you would prefer, I will transfer it to you, but I'm happy to maintain it. – deltree Dec 11 '15 at 01:07
  • 2
    Feel free to do so I don't mind. When I have time I'll also try to improve the solution with the proposal by @icl7126 and send a pull request. Also if you have improvements feel free to update my answer. – s.Daniel Dec 14 '15 at 16:20
  • How to pass element to controller like esc-key="resetFilter($event)" so in controller I can get object $event.currentTarget – tuananh May 27 '17 at 07:19
  • The "accepted answer" does work by Angular 1.7 according to the linked issue – usr-local-ΕΨΗΕΛΩΝ Oct 12 '18 at 14:25
  • FYI event.which has been deprecated https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/which. I have used `event.key === "Escape"` – LazioTibijczyk Jan 02 '20 at 14:37
11

I solve this problem like this (Controller as vm Syntax):

HTML

<input ... ng-model="vm.item" ng-keyup="vm.checkEvents($event)">

Controller

...
vm.checkEvents = function ($event) {
    if ($event.keyCode == 27) {
        vm.item = "";   
    }
}
Bijan
  • 25,559
  • 8
  • 79
  • 71
9

Listen for 'keydown' or 'keyup' events instead of 'keypress':

<input ng-model="search.query" ui-keydown="{esc: 'keyCallback($event)'}" />
bmleite
  • 26,850
  • 4
  • 71
  • 46
  • 1
    Thank you for setting me on the right path. Before writing this question I have tried 'keydown' but it didn't work in FF, so I though this wouldn't be the right solution. After your advice I also tried 'keyup' and voila it did the trick (both in FF and Chrome). – Jan Peša Feb 01 '13 at 15:00
  • You could also just do it inline: `ui-keydown="{esc: 'search.query=\"\"'}"` – ProLoser Feb 01 '13 at 19:28
  • keypress event is not triggered on Esc key-press. Using keydown/keyup will do the job. – Vivek Jul 08 '15 at 10:06
3

For now, with Angular v4, this works: (keyup.esc)="callback()"

ktretyak
  • 27,251
  • 11
  • 40
  • 63
0

I've managed to build a directive clearing directly ng-model of the input element and properly working also in Firefox. For that I need to check whether the value is already cleared (modelGetter(scope)) and also wrap the assignment to the zero $timeout method (to apply it in next digest call).

mod.directive('escClear', ['$timeout', '$parse', function($timeout, $parse) {
  return {
    link : function(scope, element, attributes, ctrl) {
      var modelGetter = $parse(attributes.ngModel);
      element.bind('keydown', function(e) {
        if (e.keyCode === $.ui.keyCode.ESCAPE && modelGetter(scope)) {
          $timeout(function() {
            scope.$apply(function () {modelGetter.assign(scope, '');});
          }, 0);
        }
      });
    }
  };
}]);

My $ property is jQuery, feel free to replace it with magic number 27.

icl7126
  • 5,740
  • 4
  • 53
  • 51
0

Angular 2 version which also updates ngModel

Directive

import { Directive, Output, EventEmitter, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[escapeInput]'
})
export class escapeInput {

  @Output() ngModelChange: EventEmitter<any> = new EventEmitter();
  private element: HTMLElement;
  private KEY_ESCAPE: number = 27;

  constructor(private elementRef: ElementRef) {
    this.element = elementRef.nativeElement;
  }

  @HostListener('keyup', ['$event']) onKeyDown(event) {
    if (event.keyCode == this.KEY_ESCAPE) {
      event.target.value = '';
      this.ngModelChange.emit(event.target.value);
    }
  }

}

Usage

<input escapeInput class="form-control" [(ngModel)]="modelValue" type="text" />
Erkin Djindjiev
  • 517
  • 4
  • 7