Angular - personalized modal HTML

I am facing a challenge where I need to trigger a popup when a button is clicked. There can be multiple buttons, each with its own overlay popup, and these popups should close when clicking outside of them. Currently, I am using TemplateRef (#toggleButton in my case) to open the popups, but it is only working for the first button and opens all popups simultaneously. Any assistance on this matter would be highly appreciated.

Feel free to check out the stackblitz link provided: https://stackblitz.com/edit/angular-d8tnwg?file=src/app/app.component.ts

Here is the HTML code:

<ng-container>
            <tr>
                <td class="custom-td" style="overflow:visible;">
                    <button #toggleButton class="icon-info" (click)="showTooltip()"></button>
                    <div #menu class="overlay-text" [ngClass]="status ? 'open' : ''">
                        <p>This is a placeholder text and will be copied
                            if you click on the button!</p>
                        <div class="form-group">
                            <input type="text">
                            <button  class="icon-filecopy"></button>
                        </div>

                    </div>
                </td>
            </tr>
            <tr>
                <td class="custom-td" style="overflow:visible;">
                    <button #toggleButton class="icon-info" (click)="showTooltip()"></button>
                    <div #menu class="overlay-text" style="background-color: yellow" [ngClass]="status ? 'open' : ''">
                        <p>This is a placeholder text and will be copied
                            if you click on the button!</p>
                        <div class="form-group">
                            <input type="text">
                            <button  class="icon-filecopy"></button>
                        </div>

                    </div>
                </td>
            </tr>
            <tr>
                <td class="custom-td" style="overflow:visible;">
                    <button #toggleButton class="icon-info" (click)="showTooltip()"></button>
                    <div #menu class="overlay-text" style="background-color: red" [ngClass]="status ? 'open' : ''">
                        <p>This is a placeholder text and will be copied
                            if you click on the button!</p>
                        <div class="form-group">
                            <input type="text">
                            <button class="icon-filecopy"></button>
                        </div>

                    </div>
                </td>
            </tr>
        </ng-container>

And here is the TypeScript code:

status: boolean = false;
  @ViewChild("toggleButton") toggleButton: QueryList<ElementRef>;
  @ViewChild("menu") menu: QueryList<ElementRef>;

  constructor(private renderer: Renderer2) {
    this.renderer.listen("window", "click", (e: Event) => {
      console.log("click outside");
      if (
        e.target !== this.toggleButton.nativeElement &&
        e.target !== this.menu.nativeElement
      ) {
        this.status = false;
      }
    });
  }

  showTooltip() {
    this.status = !this.status;
  }
}

Answer №1

The issue causing only the first button to work is the lack of unique ids. One solution suggested by rmjoia is to use

@ViewChild(Button) toggleButton: QueryList<ElementRef>
. However, it is recommended to follow best practices and ensure each button has a unique id and tooltip toggle for proper functionality.

Answer №2

In the world of programming, there is never just one solution to a problem. Each solution comes with its own set of trade-offs and considerations.

For example, you could create a function called toggle or have one value set to true and another set to false, giving you endless possibilities!

Check out this StackBlitz example forked from yours for further inspiration.

app.component.ts

import { Component } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  isTooltip1Visible = false;
  isTooltip2Visible = false;
  isTooltip3Visible = false;

  constructor() {}

  toggleTooltip(tooltipId) {
    switch (tooltipId) {
      case 1:
        this.isTooltip1Visible = !this.isTooltip1Visible;
        break;
      case 2:
        this.isTooltip2Visible = !this.isTooltip2Visible;
        break;
      case 3:
        this.isTooltip3Visible = !this.isTooltip3Visible;
        break;
      default:
        console.log("Invalid tooltip Id");
        break;
    }
  }
}

app.component.html

<table appClickOutside (clickOutside)="closeDropdown()">
    <tbody>
        <ng-container>
            <tr>
                <td class="custom-td" style="overflow:visible;">
                    <button #toggleButton class="icon-info" (click)="toggleTooltip(1)"></button>
                    <div #menu class="overlay-text" [ngClass]="isTooltip1Visible ? 'open' : ''">
                        <p>This is a placeholder text and will be copied
                            if you click on button!</p>
                        <div class="form-group">
                            <input type="text">
                            <button (click)="toggleTooltip(1)" class="icon-filecopy"></button>
                        </div>

                    </div>
                </td>
            </tr>
            <tr>
                <td class="custom-td" style="overflow:visible;">
                    <button #toggleButton class="icon-info" (click)="toggleTooltip(2)"></button>
                    <div #menu class="overlay-text" style="background-color: yellow"
                        [ngClass]="isTooltip2Visible ? 'open' : ''">
                        <p>This is a placeholder text and will be copied
                            if you click on button!</p>
                        <div class="form-group">
                            <input type="text">
                            <button (click)="toggleTooltip(2)" class="icon-filecopy"></button>
                        </div>

                    </div>
                </td>
            </tr>
            <tr>
                <td class="custom-td" style="overflow:visible;">
                    <button #toggleButton class="icon-info" (click)="toggleTooltip(3)"></button>
                    <div #menu class="overlay-text" style="background-color: red"
                        [ngClass]="isTooltip3Visible ? 'open' : ''">
                        <p>This is a placeholder text and will be copied
                            if you click on button!</p>
                        <div class="form-group">
                            <input type="text">
                            <button (click)="toggleTooltip(3)" class="icon-filecopy"></button>
                        </div>

                    </div>
                </td>
            </tr>
        </ng-container>
    </tbody>
</table>

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

Is it feasible for a React-based shell to host or load an Angular component using Module Federation in Webpack 5?

I am currently developing a web application using Angular that will be embedded or loaded from another web application built with React. I am unsure if this integration can be achieved using webpack 5's module federation. Module federation involves l ...

Running the `npm start` command in Angular tends to be quite time-consuming

When I use Visual Studio Code to run Angular projects, my laptop seems to take a longer time when running the server through npm start compared to others. Could this delay be related to my PC specifications, or is there something I can do to improve it? ...

Guide on formatting the API response using a callback function in Angular development

How can I reformat my API response using a callback function and access the data within the angular subscribe method? I attempted to use mergemap but it didn't work as expected. this.http.get('https://some.com/questions.xml', {headers, res ...

Steps to filter types by a singular property assessment

export type HalfSpin = { halfspin: string } export type FullSpin = { fullspin: string } export type SpinType = | HalfSpin | FullSpin export function isHalfSpin(_: SpinType) ...

Is there a way to dynamically adjust the size of an image in NodeJS utilizing Sharp, when only provided with a URL, employing async/await, and ensuring no local duplicate is

In my current work environment, the only image processing library available is NodeJS's Sharp for scaling images. It has been reliable due to its pipe-based nature, but now I have been given the task of converting it to TypeScript and utilizing Async/ ...

I have successfully implemented useLazyQuery in a functional component, but now I am looking to integrate it into a class component. Can you provide guidance on how to achieve

Recently, I encountered an issue with my functional component that contains 3 checkboxes and 1 button. I utilized the useLazyQuery hook to ensure that my query was only sent upon clicking the button. However, a major drawback is that my component re-rend ...

Matching an element that contains a specific string in JS/CSS

I'm currently faced with an HTML file that's being generated by an outdated system, making it tricky for me to control the code generation process. The structure of the HTML code is as follows: <table cellpadding=10> <tr> ...

Do I have to utilize npm packages?

As a newcomer to Node.js, I'm diving into the world of node features while working on an Angular 2 project. One thing I've noticed is that every plugin seems to be imported from the node_modules folder. This has me wondering - is it absolutely n ...

A TypeScript class utilizing a static variable with the singleton design pattern

I have a query regarding the most effective way to code this scenario: Within a class, I require a static variable that is accessible throughout the entire project: connection Singleton without namespace: class Broker { static connection: Connection = u ...

Setting a default value for Angular Material Autocomplete with a value extracted from a database

Is there a way to retrieve a value from a database and automatically set it as the default value in an autocomplete input field? Fetch clientTypes clientTypes: any[] = []; getClientTypes() { this.clientService.getClientTypes() .subscribe((data: a ...

Tips for optimizing Angular source code to render HTML for better SEO performance

Our web platform utilizes Angular JS for the front-end and node js for the backend, creating dynamic pages. When inspecting the code by viewing the source, it appears like this: For our business to succeed, our website needs to be SEO-friendly in order to ...

Disable the background color css when hovering over a button

I'm having trouble with my Angular and HTML code. https://i.stack.imgur.com/Ea5oV.png The image above shows that a background color appears when hovering over the first icon. I've attempted: .sidemenuitm { padding: 10px 5px; cursor: poin ...

Cannot locate: Unable to find the module '@react-stately/collections' in the Next.js application

While working on my Next.js app, I decided to add the react-use package. However, this led to a sudden influx of errors in my Next.js project! https://i.stack.imgur.com/yiW2m.png After researching similar issues on Stackoverflow, some suggestions include ...

Ways to extract values from a javascript hash map by exclusively incorporating an array

So here's the issue I'm encountering. Let's consider the following scenario: let surfaces: Map<any, any> = new Map([{"83.1" => Object}, {"84.1" => Object}]) let arr1 = ["83.1"] This is the desired o ...

Uncover the solution to eliminating webpack warnings associated with incorporating the winston logger by utilizing the ContextReplacementPlugin

When running webpack on a project that includes the winston package, several warnings are generated. This is because webpack automatically includes non-javascript files due to a lazy-loading mechanism in a dependency called logform. The issue arises when ...

A convenient utility for generating React components with pre-populated Tailwind CSS classes

When it comes to extracting local Tailwind-styled components, I tend to do it like this: const Container: React.FC = ({ children }) => ( <div className="bg-white px-5 py-5"> {children} </div> ); To simplify this process, I ...

When I try to pass a formControl to a child component in Angular, it throws a "no value

What could be causing the error message "no value accessor for form control with unspecified name" to appear? I am working with the edit-component: Here is the code in HTML: <mat-form-field> <input [formControl]="formControl"> </mat-f ...

Guide to Validating Fields in Angular's Reactive Forms After Using patchValue

I am working on a form that consists of sub-forms using ControlValueAccessor profile-form.component.ts form: FormGroup; this.form = this.formBuilder.group({ firstName: [], lastName: ["", Validators.maxLength(10)], email: ["", Valid ...

I encountered a warning while using the useViewportScroll in NextJs with Framer Motion: "Caution: The useLayoutEffect function does not have any effect on the server

Successfully implementing NextJs with Framer Motion, yet encountered a warning: Warning: useLayoutEffect does not function on the server due to its effect not being able to be encoded in the server renderer's output format. This may cause a differenc ...

Is there a way for me to access the data stored in session storage in Next.js?

One of the components in my project is a slider, which allows users to set the number of columns in an Image Gallery component. This code snippet shows the implementation of the slider component: export default function Slider({ value, handleChange }: ISl ...