是否可以在服务中使用 HostListener?或者如何在 Angular 服务中使用 DOM 事件?
Is it possible to use HostListener in a Service? Or how to use DOM events in an Angular service?
我想创建一个服务来检测所有键盘输入,将击键转换为基于可配置映射的操作,并公开各种元素可以绑定到的可观察对象以对特定按键做出反应。
到目前为止,以下是我的代码的简化,当 HostListener 在组件中时它可以工作,但现在我将它移到一个服务中,即使它已被初始化,它也永远不会触发。在服务中无法检测到这样的输入吗?
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);
}
}
似乎无法在服务中使用 HostListener
。
更新
喜欢 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$));
}
}
现场演示:https://stackblitz.com/edit/angular-so4?file=src%2Fapp%2Fmy-mouse.service.ts
旧
您可以使用旧方法 window.addEventListener
就像@yurzui 已经指出的那样。
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.
原回答:
还有另一种方法,使用 RendererFactory2
and Renderer2
。
我正在使用这样的服务来监视闲置并相应地注销用户。
这是代码的一部分:
@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;
})
);
}
}
通过将 null 传递给渲染器工厂 this.rendererFactory2.createRenderer(null, null)
,您可以获得默认的 DOMrenderer,因此可以监听 window 事件。
另一种可以使用的方法是使用 rxjs
提供的 fromEvent
函数。
@Injectable()
export class InputService {
keyboardInput = fromEvent(window, 'keydown').pipe(
tap(i => console.log('here', i))
)
constructor() {
// Subscribe to the property or use the async pipe.
// Remember to unsubscribe when you are done if you don't use the async pipe
this.keyboardInput.subscribe();
}
}
我想创建一个服务来检测所有键盘输入,将击键转换为基于可配置映射的操作,并公开各种元素可以绑定到的可观察对象以对特定按键做出反应。
到目前为止,以下是我的代码的简化,当 HostListener 在组件中时它可以工作,但现在我将它移到一个服务中,即使它已被初始化,它也永远不会触发。在服务中无法检测到这样的输入吗?
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);
}
}
似乎无法在服务中使用 HostListener
。
更新
喜欢
@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$));
}
}
现场演示:https://stackblitz.com/edit/angular-so4?file=src%2Fapp%2Fmy-mouse.service.ts
旧
您可以使用旧方法 window.addEventListener
就像@yurzui 已经指出的那样。
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.
原回答:
还有另一种方法,使用 RendererFactory2
and Renderer2
。
我正在使用这样的服务来监视闲置并相应地注销用户。
这是代码的一部分:
@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;
})
);
}
}
通过将 null 传递给渲染器工厂 this.rendererFactory2.createRenderer(null, null)
,您可以获得默认的 DOMrenderer,因此可以监听 window 事件。
另一种可以使用的方法是使用 rxjs
提供的 fromEvent
函数。
@Injectable()
export class InputService {
keyboardInput = fromEvent(window, 'keydown').pipe(
tap(i => console.log('here', i))
)
constructor() {
// Subscribe to the property or use the async pipe.
// Remember to unsubscribe when you are done if you don't use the async pipe
this.keyboardInput.subscribe();
}
}