Steps to keep a filter component open when engaging with a calendar component in Angular and avoid the click outside event from closing it

I am currently developing an Angular application that includes a filter component with visibility toggled based on a click outside event. While this functionality works correctly, I encountered an issue when incorporating a calendar component within the filter area. Interacting with the calendar (e.g., selecting dates) triggers the click outside event, causing the filter component to close prematurely.

Below is the code snippet for my filter component:

...

  showFilter = false;
  private globalClickListener: () => void;

  // List of classes to ignore during the click outside check
  ignoredClasses = [
    '.mat-select', 
    '.mat-datepicker', 
    'span.mat-option-text', 
    'span.mat-calendar-body-cell-content.mat-focus-indicator.mat-calendar-body-selected', 
    'button.mat-calendar-previous-button.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base.cdk-focused.cdk-mouse-focused', 
    'button.mat-calendar-next-button.mdc-icon-button.mat-mdc-icon-button.mat-unthemed.mat-mdc-button-base.cdk-focused.cdk-mouse-focused',
    '.mat-calendar-body-cell-content.mat-calendar-body-selected' 
  ];

  constructor(
    ...
    filterService
      .openCloseFilterEvent()
      .subscribe(() => (this.showFilter = !this.showFilter));
    filterService.dataFilterEvent().subscribe(() => (this.showFilter = false));
  }

  ngOnInit() {
    this.globalClickListener = this.renderer.listen('document', 'click', (event: Event) => {
      const clickedElement = event.target as HTMLElement;
      const isIgnoredElement = this.ignoredClasses.some(selector => clickedElement.closest(selector));
      if (!this.el.nativeElement.contains(event.target) && !isIgnoredElement) {
        this.showFilter = false;
      }
    });
  }

  ngOnDestroy() {
    if (this.globalClickListener) {
      this.globalClickListener();
    }
  }

  searchEmitter(event: Event) {
    event.stopPropagation();
    this.filterService.setPage(25, 0);
    this.filterService.updateFilter(this.filter);
    this.showFilter = false;
  }
}

Despite attempting to use ignoredClasses/isIgnoredElement as a solution, it did not yield the expected results. This may be due to the fact that the calendar component is not directly related in hierarchy to the filter component. The calendar and other components are located in separate files, unlike in my previous project where a similar approach worked seamlessly.

If anyone has insights on how to address this issue or suggest alternative solutions, I would greatly appreciate your input. I have tried utilizing the ignoredClasses both as an array and individually, but I am open to exploring other possibilities.

Answer №1

Typically, when dealing with the concept of clicking outside, you do not need to check for specific classes; instead, you locate the exact HTMLElement that allows the click event.

When working with an Angular element, using ViewChild can be useful. However, when it comes to a mat-datepicker component, it utilizes cdk-overlay (which is positioned outside the application). Therefore, we can create a function to retrieve the respective htmlElement.

  @ViewChild('div') div:ElementRef; //<--refers to our element
  subscription!:Subscription  //<--can be used with fromEvent rxjs operator

  getCdkOverlayContainer(){
    const overlayContainer= document.getElementsByClassName('cdk-overlay-container')
    if (!overlayContainer )
      return null;

      const calendar=document.getElementsByTagName('mat-calendar')
      const overlay=Array.from(overlayContainer)
                           .filter((x:any)=>x.contains(calendar[0]))
    return overlay && overlay.length?overlay[0]:null
  }

  //unsubscribe in ngOnDestroy method
  ngOnDestroy()
  {
    this.subscription && this.subscription.unsubscribe;
  }
  ngOnInit()
  {
    this.subscription=fromEvent(document,'click').pipe(
      //use filter to avoid execution
      //if the nativeElement of the div contains the target or
      //the cdkOverLay contains the target
      filter((x:any)=>
    {
      const overlay=this.getCdkOverlayContainer(); //retrieve the overLay
      return (!this.div || !this.div.nativeElement.contains(x.target) && 
             (!overlay || !overlay.contains(x.target))
    })).
      subscribe(_=>
        {
          ...perform necessary actions...
        })
  }

Check out a live example on StackBlitz

Answer №2

Have you applied the "ag-custom-component-popup" class to the floating element?

If not, make sure to include panelClass="ag-custom-component-popup" in the mat-date-range-picker component.

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

Angular2-starter configuration setup with environment-based variables (such as .env or .conf) for testing and production settings

Frameworks like PHP Laravel often include files for local configuration, separate from dev, test, and production environments. How can a configuration file be provided for an angular-starter project that contains all local environment variable values (su ...

Angular's DecimalPipe will truncate any strings that exceed 10 digits

Using the decimal pipe to format numbers in an input field value| number:'0.0-6': 'en-us' When working with numbers containing more than 10 digits, it displays as follows: For 11111111111.123456, it formats to 11,111,111,111.123455 ...

Is there a way to establish a data type using a specific key within the Record<K, T> structure in Typescript?

Imagine the scenario where an object type needs to be created and linked to a specific key specified in Record<Keys, Type>. Let's say there is a type called User, which can have one of three values - user, admin, or moderator. A new type called ...

Numerous instances of a specific custom directive in Angular2

I'm in the process of developing an Angular application, focusing on creating highly reusable code using directives (as components). <div class="container container-fluid" > <div class="row"> <table class="table table-responsive ...

Avoid accessing invariant variables when mocking: __extends

I'm currently in the process of converting a collection of react components from JavaScript to TypeScript, and I've encountered an issue with jest.mock(). Before: "react": "^15.6.1" "jest": "20.0.4" "enzyme": "^2.9.1" CustomDate.js ... import ...

What is the best method for enabling browser zoom capabilities within playwright?

Is there a method to adjust the browser zoom level while running an end-to-end test? I attempted the following code without success. await page.keyboard.down('Control'); for (let i = 0; i < 7; i++) { await page.keyboard.press('+&apo ...

If a task is currently ongoing, be sure to subscribe to it; otherwise, restart it

If I have a long-running observable called longObservable, which emits a new string once after 5 seconds and then completes. longObservable(): Subject<string> { return timer(5000).pipe{ map(() => randomString()) } } Other pages c ...

Retrieve Data from Angular's ngRedux Store

Wanting to retrieve and react to changes in data from ngRedux-store, I am looking to utilize it in a guard with the CanActivate interface. In my previous method of fetching data, I typically used this code snippet: @select(['auth', 'IsAuth ...

Jest is having trouble recognizing a custom global function during testing, even though it functions properly outside of testing

In my Express app, I have a custom function called foo that is globally scoped. However, when running Jest test scripts, the function is being recognized as undefined, causing any tests that rely on it to fail. This is declared in index.d.ts: declare glob ...

Issue encountered when attempting to manipulate Observable data with the map function

Having trouble getting a list from HttpClient in Angular due to issues with transforming the data from an Observable. Even after testing with static values, the results remain unchanged. Attempts at transforming values using map() within an Observable res ...

Customize column header values for Excel/CSV export with ag-grid

In my application, I am utilizing ag-grid which includes two buttons 'Download Excel' and 'Download CSV' at the top of the grid. When a user clicks on either button, the data from the grid should be downloaded in the respective file for ...

Building a VTK.js mesh using a personalized data source

I am currently working on displaying a mesh in a react ts app using @kitware/vtk.js 18.1.2 along with a custom source. Initially, I successfully set up a scene with a cone source like this: // (...) const coneSource = vtkConeSource.newInstance(); const m ...

Resolve the Angular proxy issue with the backend

Attempting to troubleshoot a similar problem, but struggling to understand why it's not functioning correctly. The API I am working with is located at: localhost:8080/api/acl/authorize Here is the HTTP client code: const AUTH_URI = "/api/acl/&q ...

Transport parameter via routerLink to a different component

When I click on the link provided in the list-profile HTML document, I need to send a variable. However, the current code is not working and causing crashes. If more details are required, feel free to ask. List Profile Component HTML <div class="col c ...

Conditional application of Angular animations is possible

After implementing the fadein effect from Angular-Animations in my ASP.NET based Angular project, I encountered an issue where only the first row is faded-in while the other rows are not displayed when using *ngIf. Here is a snippet of the code: <ng-te ...

The Clerk middleware is causing delays in loading, leading to a 504 Error on NextJS / Vercel with the message 'FUNCTION_INVOCATION_TIMEOUT'

I'm currently working on a web page where I need to utilize Clerk for authentication and login. However, I've encountered an issue with the middleware taking too long to load, causing deployment problems for the app. Here is the code from middle ...

Having trouble installing memlab using the npm package

Recently, I made an attempt to install the memlab library from Meta's GitHub. Initially, when I installed it without using the -g flag, the installation was successful. However, I encountered an issue where I could not execute any of the memlab comman ...

Encountering a 404 error while trying to deploy a React app on Verc

After deploying my React project with TypeScript using Vite, everything is working smoothly. However, I am encountering a 404 error when trying to refresh the page. Error: 404 NOT_FOUND Error Code: NOT_FOUND ...

javascript identify dissimilarities within arrays

Working on an Angular 2 application and attempting to identify the difference between two arrays (last seven days and missing dates within the last seven days). Everything works fine when initializing the array through a string, like in example code 1. How ...

React—conditionally rendering components

How come my page doesn't update when I change the userType state by clicking on the Bootstrap buttons? import * as React from 'react'; import {ButtonToolbar, ToggleButtonGroup, ToggleButton} from 'react-bootstrap'; import CreateP ...