Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use HostListener in a Service? Or how to use DOM events in an Angular service?

I want to create a service which detects all keyboard input, translates the key strokes into actions based on a configurable mapping, and exposes observables which various elements can bind to to react to specific key presses.

The following is a simplification of my code so far, it worked when HostListener was in a component, but now I've moved it into a service it never fires even though it is definitely initialised. Is it not possible to detect input like this in a service?

import { Injectable, HostListener } from '@angular/core';  import { Subject } from 'rxjs/Subject';  @Injectable() export class InputService {      @HostListener('window:keydown', ['$event'])     keyboardInput(event: any) {         console.log(event);     } } 
like image 245
trelltron Avatar asked Sep 20 '16 11:09

trelltron


People also ask

Can we use HostListener in service?

Solution. Seems like its not possible to use HostListener in a service.

Can we use HostListener in component?

@HostBinding and @HostListener are two decorators in Angular that can be really useful in custom directives. @HostBinding lets you set properties on the element or component that hosts the directive, and @HostListener lets you listen for events on the host element or component.

What is the use of HostListener in Angular?

HostListenerlink Decorator that declares a DOM event to listen for, and provides a handler method to run when that event occurs.

Which of the following is used for listening to user events in Angular?

@HostListener is Angular's decorator method that's used for listening to DOM events on the host element of both component and attribute directives.


2 Answers

Seems like its not possible to use HostListener in a service.

UPDATE

like Stanislasdrg Reinstate Monica wrote, there's a more elegant and more angular way using the renderer..

@Injectable() export class MyMouseService implements OnDestroy {   private _destroy$ = new Subject();    public onClick$: Observable<Event>;    constructor(private rendererFactory2: RendererFactory2) {     const renderer = this.rendererFactory2.createRenderer(null, null);      this.createOnClickObservable(renderer);   }    ngOnDestroy() {     this._destroy$.next();     this._destroy$.complete();   }    private createOnClickObservable(renderer: Renderer2) {     let removeClickEventListener: () => void;     const createClickEventListener = (       handler: (e: Event) => boolean | void     ) => {       removeClickEventListener = renderer.listen("document", "click", handler);     };      this.onClick$ = fromEventPattern<Event>(createClickEventListener, () =>       removeClickEventListener()     ).pipe(takeUntil(this._destroy$));   } } 

live-demo: https://stackblitz.com/edit/angular-so4?file=src%2Fapp%2Fmy-mouse.service.ts

OLD

You could use the old way window.addEventListener like @yurzui pointed out already.

https://plnkr.co/edit/tc53cvQDfLHhaR68ilKr?p=preview

import {Component, NgModule, HostListener, Injectable} from '@angular/core' import {BrowserModule} from '@angular/platform-browser'  @Injectable() export class MyService {    constructor() {     window.addEventListener('keydown', (event) => {       console.dir(event);     });   }  }  @Component({   selector: 'my-app',   template: `     <div>       <h2>Hello {{name}}</h2>     </div>   `, }) export class App {    constructor(private _srvc: MyService) {     this.name = 'Angular2'   } }  @NgModule({   imports: [ BrowserModule ],   declarations: [ App ],   providers: [MyService],   bootstrap: [ App ] }) export class AppModule {} 
like image 99
slaesh Avatar answered Oct 06 '22 14:10

slaesh


Caution:
Lookout for memory leaks as the listeners don't automatically stop listening.

Original answer:
There is an other way of doing so, by using RendererFactory2 and Renderer2. I am using such a service to monitor idleness and logout the user accordingly. Here is part of the code :

@Injectable() export class IdleService {    renderer: Renderer2;   lastInteraction: Date = new Date();   definedInactivityPeriod = 10000;    constructor(     private rendererFactory2: RendererFactory2,     private auth: AuthService,     private router: Router   ) {     this.renderer = this.rendererFactory2.createRenderer(null, null);     this.renderer.listen('document', 'mousemove', (evt) => {       console.log('mousemove');       this.lastInteraction = new Date();     });     // Subscribing here for demo only     this.idlePoll().subscribe();   }    idlePoll() {     return interval(1000)       .pipe(         tap(() => console.log('here', new Date().getTime() - this.lastInteraction.getTime())),         takeWhile(() => {           if ((new Date().getTime() - this.lastInteraction.getTime()) > this.definedInactivityPeriod) {             this.auth.logout();                                   }           return (new Date().getTime() - this.lastInteraction.getTime()) < this.definedInactivityPeriod;         })       );   }  } 

By passing null to renderer factory this.rendererFactory2.createRenderer(null, null) you get a hold of the default DOMrenderer and can therefore listen to window events.

like image 37
Standaa - Remember Monica Avatar answered Oct 06 '22 15:10

Standaa - Remember Monica