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); } }
Solution. Seems like its not possible to use HostListener in a service.
@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.
HostListenerlink Decorator that declares a DOM event to listen for, and provides a handler method to run when that event occurs.
@HostListener is Angular's decorator method that's used for listening to DOM events on the host element of both component and attribute directives.
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 {}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With