3
votes

I have two components - Parent and Child component. I have attached hostlistener to both component to detect key events. I want to detect space key event in both components. If user presses space key in child component text field then i want parent component to do nothing. But if user is not in child component text field and presses space key then i want parent component to trigger a function.

export class ParentComponent {

 constructor() { }

 @HostListener('window:keyup', ['$event'])
  keyEvent(event: KeyboardEvent) { 
  console.log("PARENT", event.keyCode);
 }
}

export class ChildComponent {

 constructor() { }

 @HostListener('window:keyup', ['$event'])
  keyEvent(event: KeyboardEvent) { 
  console.log("CHILD", event.keyCode);
 }
}

Here events are getting captured but in specific order - first PARENT and then CHILD. What i want these events to be - first captured by CHILD so that i can decide what to do in parent component or can stop event propagation.

1
Not sure if this is relevant - github.com/angular/angular/issues/11200 - jaibatrik
why can't you use the keyup event listener? <input id="child" (keyup)="onChildClick()" /> <input id="parent" (keyup)="onParentClick()" />? - Woot
@Woot, i needed a window listener for key events. - Rj-s

1 Answers

4
votes

If we add same listener event in both parent and child component there are two possibilities.

  1. If user triggers from parent component, only the event in parent component will be executed
  2. If user triggers from child component, both the events in child and parent component will be executed. remove window from listener events so child component will be executed first before the parent component.

If you don't want to execute parent from child, pass some variables to parent to control the events.

Working demo, I have used escape event as sample

Parent component

import { Component } from '@angular/core';
import {  HostListener } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  color : String='green';
  child : Boolean=true;

  @HostListener('keydown', ['$event']) triggerEsc(e: KeyboardEvent) {

    if(e.keyCode===27 && this.child===true){
      console.log("global esc");
      alert("parent esc");
    }else{
      this.child=true;
    }
  }

  public doSomething(child: any):void {
    this.child=child;
}

  name = 'Angular 5';
}

Parent template

<input placeholder="Parent Can't copy/paste" type="text" appBlockCopyPaste/>
<hello (child)="doSomething($event)"></hello>

Child component with template

import { Component, Input,Output , EventEmitter} from '@angular/core';
import {  HostListener } from '@angular/core';

@Component({
  selector: 'hello',
  template: `<input placeholder="Child" type="text" appBlockCopyPaste/>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Output() child: EventEmitter<any> = new EventEmitter<any>();

  @HostListener('keydown', ['$event']) triggerEsc(e: KeyboardEvent) {
    if(e.keyCode===27){
      this.child.emit(false);
      console.log("global esc");
      alert("child esc");
    }
  }
}