Hear and register keypress in Angular service

I offer a dialog service

Check it out below

 @Injectable()
export class HomeDialogService {
  public constructor(private readonly dialogService: DialogService, private readonly userAgent: UserAgentService) {}

  @HostListener('document:keydown.escape', ['$event']) onKeydownHandler(event: KeyboardEvent) {
    console.log(event);
  }

  public open<TComponentParams, TResult>(
    content: Type<DialogBaseComponent<TComponentParams, TResult>>,
    params?: TComponentParams,
    dialogOptions?: HomeDialogSettings | undefined,
  ): { dialogRef: DialogRef; result$: Observable<IDialogResult<TResult>> } {
    const desktopDialogWidth = dialogOptions.kendoDialogSettings.width ?? DESKTOP_DEFAULT_DIALOG_WIDTH;
    const desktopDialogMaxHeight = dialogOptions.kendoDialogSettings.maxHeight ?? DESKTOP_MAX_POPUP_HEIGHT;

    const isMobile = this.userAgent.mobile;

    const dialogRef = this.dialogService.open({
      content,
      ...dialogOptions.kendoDialogSettings,
      width: isMobile ? WIDTH_FULL : desktopDialogWidth,
      maxHeight: isMobile ? MOBILE_MAX_POPUP_HEIGHT : desktopDialogMaxHeight,
    });

    const component = dialogRef.content.instance as DialogBaseComponent<TComponentParams, TResult>;

    if (params) {
      component.params = params;
    }

    if (isMobile) {
      this.configureDialogStylesForNonDesktop(dialogRef);
    }

    const result$ = dialogRef.result.pipe(
      map((result: DialogCloseResult | TResult) => {
        if (result instanceof DialogCloseResult) {
          return {
            status: DialogResultStatus.Cancel,
          };
        }

        return { status: DialogResultStatus.Ok, data: result };
      }),
    );

    return {
      dialogRef,
      result$,
    };
  }
}

I have added a listener for the escape button click and injected it into the component like this

export class SdTicketComponent {
  @Input()
  public ticket!: ServiceDeskTicket;

  constructor(
    public readonly userAgent: UserAgentService,
    public readonly startScreenSdTicketsSectionService: StartScreenSdTicketsSectionService,
    private readonly serviceDeskService: ServiceDeskService,
    private readonly dialogService: HomeDialogService,
    private readonly alertService: AlertService,
  ) {}

   

  public onTicketClick(): void {
    this.dialogService
      .open(
        SdTicketPopupComponent,
        { ticket: this.ticket },
        { kendoDialogSettings: { title: this.ticket.summary, width: 880, cssClass: 'sd-ticket-popup-wrapper' } },
      )
      .result$.pipe(
        filter(result => result.status === DialogResultStatus.Ok),
        map(result => result.data as IServiceDeskIssueRateData),
        switchMap(result => this.serviceDeskService.closeTicket(this.ticket, result)),
        switchMap(() => this.startScreenSdTicketsSectionService.initSectionData(true)),
        untilDestroyed(this),
      )
      .subscribe(_ => {
        this.alertService.showSuccess(`${this.ticket.key} has been closed`);
      });
  }
}

However, when I press the escape key, nothing shows up in the console

If I move the code for the HostListener to the component, everything works fine.

Any ideas on how I can make it work from the service?

Answer №1

It is important to remember that the HostListener must be specified within a component.

If you desire to monitor key events throughout the entire application, follow these steps:

  1. Add your HostListener in your app.component.ts, for instance.

  2. Create an Observable in your service to transmit the key events. Use

    keyEvents$ = new BehaviorSubject(null)

  3. In your app.component.ts, when using HostListener, utilize your service and include keyEvents$.next(event)

  4. In the component where you wish to capture the event, import the service and subscribe to any modifications.

    keyEvents$.pipe(map(event=> event.key ==="escape")).subscribe(...)

Answer №2

Utilize Angular's Renderer2 for maintaining your strategy.

For instance:

private renderer: Renderer2;
private unlistener: () => void;

public constructor(private rendererFactory: RendererFactory2, private readonly dialogService: DialogService, private readonly userAgentService: UserAgentService) {

  // when in a service, make use of the renderer factory
  this.renderer = this.rendererFactory.createRenderer(null, null);
  
  // begin listening and save the "unlisten" function
  this.unlistener = this.renderer.listen('document', 'keydown.escape', () => {
      // insert your logic here
      console.log(event);
  }); 

}

ngOnDestroy() {
  // will stop listening on service destruction (adjust as needed)
  this.unlistener();

}

Reference:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

JavaScript JSON conversion from a list

If I have two different lists, such as: list1= ['blue', 'green', 'purple'] list2 = ['circle', 'square', 'triangle'] Is there a way to convert them into a JSON object structure by using another l ...

Looking to verify the functionality of the router.navigate method when employing relativeTo?

How can we effectively test the router.navigate method to ensure it accepts provided queryParams and navigates to the correct location path? In Component file:-- syncQueryParams() { this.router.navigate([], { relativeTo: this.activatedRoute, ...

What is the best way to incorporate an automatic scroll to the following section on a single page website

My goal is to implement a feature that enables automatic scrolling between sections as the user scrolls up or down. The smooth transition should occur when the user reaches halfway through each section, seamlessly moving to the next screen on the same page ...

What are some ways to create a dynamic child server component?

Take a look at the following code snippet // layout.tsx export default function Layout({children}: any) { return <div> {children} </div> } // page.tsx export const dynamic = "force-dynamic"; const DynamicChild = dynamic( ...

Show the CustomError message and HTTP status code that was raised by the server in the HttpErrorResponse

I am in the process of developing an ASP.NET Core API server paired with an Angular client. One of my main objectives is to ensure that the client can effectively capture any exceptions thrown by the server. I have implemented an HTTP Interceptor in Angula ...

Issue: Module 'xml2json' not found

Encountered an error while running my project package. When I tried to install the necessary packages using npm install xml2json I still encountered another error below. Can anyone provide suggestions or ideas on how to resolve this issue? D:\xa ...

"Encountered an error in Angular: ContentChild not found

Working with Angular 5, I am attempting to develop a dynamic component. One of the components is a simple directive named MyColumnDef (with the selector [myColumnDef]). It is used in the following: parent.compontent.html: <div> <my-table> ...

Retrieve text excluding a specific class or id using jQuery

I need help with extracting text from an HTML div. <div id="example"> I am writing a <span class='outer'>query for help <span class='inner'>on a coding forum</span></span> </div> const te ...

Infinite loop triggered by jQuery dropdown menu on page resize was causing an issue

I have been working on developing a navigation menu for a website that displays as a horizontal bar on larger screens, but transforms into a jQuery dropdown menu when the window width is less than 980px. During initial page load with a window width below ...

One issue encountered in the AngularJS library is that the default value does not function properly in the select element when the value and

In this AngularJS example, I have created a sample for the select functionality. It is working fine when I set the default value of the select to "$scope.selectedValue = "SureshRaina";". However, when I set it to "$scope.selectedValue = "Arun";", it does n ...

What is the process for creating a selector that links to an element that has been dynamically generated?

Can anyone help me figure out how to create a selector that links to a dynamically created element without using an event on the dynamic element? By dynamically created element, I mean an element that is not present in the HTML at the beginning. I know t ...

What is the mechanism of `this` in higher order components?

I am delving into the concept of higher order components by exploring this online resource. However, I am struggling to grasp how this is utilized within one. Am I correct in thinking that the this in the constructor refers to what will ultimately be retur ...

Is there a way to determine if the tab has been muted by the

Chrome enables users to easily mute tabs by right-clicking on them and selecting Mute Tab. I am curious about whether there is a method to detect when a user has muted the tab. In other words, I am interested in knowing if it's possible for a website ...

The font size appears significantly smaller than expected when using wkhtmltoimage to render

I am trying to convert text into an image, with a static layout and size while adjusting the font size based on the amount of text. I prefer using wkhtmltoimage 0.12.5 as it offers various CSS styling options. Currently, I am working on a Mac. Below is a ...

Encountering a "focus" error with React-Native-Phone-Input library, where the property is null

For my project, I decided to incorporate the react-native-phone-input library. Everything was going smoothly until I encountered an issue with their focus function. Initially, it worked perfectly fine, but subsequently, when I attempted to input a phone nu ...

Is it possible to create a class object with properties directly from the constructor, without needing to cast a custom constructor signature

class __Constants__ { [key: string]: string; constructor(values: string[]) { values.forEach((key) => { this[key] = key; }); } } const Constants = __Constants__ as { new <T extends readonly string[]>(values: T): { [k in T[num ...

What is the best way to prevent two paths within an SVG from intersecting with each other?

Currently, I am developing a program that allows the user to draw lines. Once the user completes drawing a line, I receive an array of points in this format: [[x, y], [x, y], ...]. I then convert these points into a path string using the following functio ...

Vue allows you to easily generate child div elements within a parent

Having some issues creating a child div with Vue. The code is being placed correctly, but it's being stored as an array. <template> <div class="containers" v-bind:style="{ backgroundColor: pageStyle.backgroundColor, paddingLeft:'5%& ...

Add the item and increase its value within an array using AngularJS

http://plnkr.co/edit/NDTgTaTO1xT7bLS1FALN?p=preview <button ng-click="addRow()">add row</button> <div ng-repeat="row in rows"> <input type="text" placeholder="name"><input type="tel" placeholder="tel"> </div> I am cur ...

Tips for efficiently combining mergeMap observables and providing a singular value for the entire observable

Consider this particular case involving TypeScript/angular with rxjs 6.5: main(){ const items = ['session', 'user']; const source: Observable<any> = from(items); source .pipe( ...