Angular 2 - Component's Off-click Feature Falters

Achieving a desired effect using Angular 2, I have implemented a component with a small popup <div>. The popup is dismissed when the user clicks anywhere on the document except for the popup itself. To achieve this functionality, I utilize HostListener.

While this implementation works properly when there are no interior elements within the popup, it fails to behave as desired if there are any interior elements present. In other words, clicking on the popup will accidentally dismiss it.

The following code snippet showcases the functioning implementation:

@Component({
  selector: 'my-app',
  template: `
    <h4 class="example" (click)="showPopup = true">
      Click to show a correctly working popup
    </h4>

    <div *ngIf="showPopup" class="example popup">
      Click anywhere besides here to dismiss me
    </div>
  `,
})
export class App {
  public showPopup = false;

  @HostListener('document:click', ['$event']) showThePopup(e: Event) {
    if (!e.target.classList.contains('example')) {
      this.showPopup = false;
    }
  }
}

However, modifying the template in the following manner will result in a failure:

@Component({
  selector: 'my-app',
  template: ` 
    <h4 class="example" (click)="showPopup = true">
      Click to show a popup that will fail
    </h4>

    <div *ngIf="showPopup" class="example popup">
      <p>Clicking here will dismiss me, which should not happen</p>
    </div>
  `,
})

What coding approach could be adopted to ensure the correct behavior of the code?

To access the working Plunker version, please follow the link: http://plnkr.co/edit/7viqp6bR8LesM7lCsNQW?p=preview

Answer №1

If you want to make your life easier, I recommend separating the popup into its own component instead of trying to find if any parent element has the popup class.

Take a look at this functional Plunker that demonstrates the concept.


The Reasoning

When the popup is in its own component, you can access the native element of the entire component by using ElementRef. Then, you can check whether it contains the target of the click event.

We use the mousedown event so that it executes before the (click) event is triggered.

@HostListener('document:mousedown', ['$event']) showThePopup(e: Event) {
    if(!this._eref.nativeElement.contains(e.target)){
        this.showPopup = false;
    }
}

You expose the variable _eref by adding the definition to your constructor:

constructor(private _eref: ElementRef) { }

In the parent component, you can utilize local variables and <ng-content> to display the popup:

app.html

<h4 class="example" (click)="mypopup.show()">
  Click to show a popup that works as expected.
</h4>

<popup #mypopup>
  <div class="inside"><p>This is a Popup</p></div>
</popup> 

popup html

<div *ngIf="showPopup" class="example popup">
  <ng-content></ng-content>
</div>

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

Reactive forms in Angular do not refresh or update automatically when a new error is added

Upon initializing my FormGroup in the ngOnInit() method, I invoke a validator function to ensure that the password and confirmPassword fields match. This is how it looks in TypeScript: regForm: FormGroup; constructor() { } ngOnInit() { this.regFo ...

Encountering an error in Angular 8 where attempting to access an element in ngOnInit results in "Cannot read property 'focus' of null"

My html code in modal-login.component.html includes the following: <input placeholder="Password" id="password" type="password" formControlName="password" class="form-input" #loginFormPassword /> In m ...

'Android users experiencing issue with custom marker icons failing to display'

I have the following code snippet: this.service.getListe(data).forEach((data: any) => { const marker: Marker = this.map.addMarkerSync(data); marker.on(GoogleMapsEvent.MARKER_CLICK).subscribe((params) => { this.click(params); }); ...

Testing the functionality of NgRx effect through unit testing

Looking to unit test a functional effect found in this code snippet export const loadUsers = createEffect( (actions$ = inject(Actions), usersService = inject(UsersService)) => { return actions$.pipe( ofType(userActions.getUser), exhaus ...

Angular displays the error message TS2339, stating that the property 'handleError' is not found on the 'HeroService' type

Hey everyone, I know there are already a few questions out there about Typescript compilation errors, but I'm facing a unique challenge that I can't quite figure out. I'm currently working on the Angular Tour of Heroes app and trying to com ...

I'm looking to find the Angular version of "event.target.value" - can you help me out?

https://stackblitz.com/edit/angular-ivy-s2ujmr?file=src/app/pages/home/home.component.html I am currently working on getting the dropdown menu to function properly for filtering the flags displayed below it. My initial thought was to replicate the search ...

What is the best way to activate a click event when I set a radio button to checked?

I am facing an issue with an uninitialized property in my app.component.ts: color!:string; I am trying to automatically initialize the color property when a radio button is selected: <div> <input type="radio" name="colors" ( ...

Issue with setInterval function execution within an Angular for loop

My goal is to dynamically invoke an API at specific intervals. However, when attempting to utilize the following code snippet in Angular 7, I encountered issues with the interval timing. I am seeking a solution for achieving dynamic short polling. ngOnIn ...

The elements on the webpage are spilling over with content

I encountered an issue while creating a dashboard with a sidebar on the left side. When adding content to the page, some of it ended up hidden behind the sidebar. I tried using overflow-x:auto and this was the result: https://i.stack.imgur.com/5qHJY.jpg Be ...

Integrating a packaging NPM functionality into Angular for streamlined development

After completing a software engineering assignment, I am struggling with the final requirement. I need to implement an NPM packaging command called "npm build" to compile and publish the front end developed in Angular to the backend project. Initially, I t ...

How can I retrieve the current table instance in NGX-Datatables?

I have a component that includes a table. This table receives RowData from another component through the use of @Input. How can I access the current instance of this table? Here is a snippet of my HTML: <ngx-datatable class="material" ...

The ng2-material library encountered an error: TypeError - the function all_2.Media.hasMedia is not defined

I am encountering issues while trying to integrate ng2-material with Angular 2. Specifically, when utilizing the Sidenav component, I am faced with the following errors: An exception occurred: TypeError: all_2.Media.hasMedia is not a function in [hasMedi ...

Mapping the preselected values of multiple input fields into an array in Angular 6: A step-by-step guide

When submitting a form with input fields, I need to capture the selected values into an array format like {"userid":1,"newstatus":[1],"mygroup":[1,2,3]}. I attempted using ngmodel but encountered issues. Below is the code snippet: home.component.html & ...

Interacting between two Angular 4 applications

I came across solutions here and here related to this issue, but they seem to be for an older beta version of Angular (I believe now we should bootstrap a module, not a component; also, I couldn't find the bootstrap function in the documentation for v ...

Generating lasting and distinctive hyperlinks

Currently, I am in the process of developing an application that enables users to search for and compile a collection of their preferred music albums. At this stage, users have the capability to create their personalized list. However, my next objective i ...

Issues with updating values in Angular form controls are not being resolved even with the use of [formControl].valueChanges

[formControl].valueChanges is not triggering .html <span>Test</span> <input type="number" [formControl]="testForm"> .ts testData: EventEmitter<any> = new EventEmitter<any>(); testForm: FromCo ...

Trouble with scrolling on Kendo chart while using mobile device

I am facing an issue with multiple kendo charts on my website. These charts have panning and zooming enabled, but in the mobile view, they take up 100% of the width which causes touch events to not work properly for scrolling. I attempted to attach an even ...

Customizing Angular Material select fields with border radius

I attempted to adjust the select field by adjusting the border radius, but it doesn't seem to be taking effect. I've made changes in the general style.css file, but so far, the issue remains unresolved. ...

What is the process for including a resource parameter in the acquireTokenSilent method of an MSAL instance within an Angular application?

const requestToken = { permissions: ['view_only'], }; newToken = await this.authInstance.acquireTokenSilent(requestToken); I'm trying to include the client ID of my Web API as a resource parameter when requesting the access token. Strugg ...

What is the best way to pass the answerId in the action that I am dispatching, when the answerId is nested within an array object within another array object in

Reflect on the following: private listenToAnswerDeleted() { this.uiService.onGlobalEvent('ANSWER_DELETED').subscribe((response) => { this.store.dispatch(deleteAnswerAction({'answerId': })); }); } Upon receiving a respon ...