Display a popup in Angular 4 when a click event is triggered by a separate

I am currently facing an issue that I can't seem to solve. My objective is to display a popup div on the page when clicking on a menu entry in my navbar.component.

In order to achieve this, I introduced a property called "show" in my popup component which should add the "show" class to my div using the ngClass directive with an if condition. While I can successfully implement this feature if the button action is within the popup component itself, I'm unable to trigger the show class when clicking on another component. Although the property in the Object gets updated, the class remains unprinted. I'm working with Angular 4 and using ng-bootstrap. I've attempted various approaches including services and parent/child emit events.

Here's how my project is structured:

app.component.html

<app-nav-bar></app-nav-bar>
<app-login></app-login>
<router-outlet></router-outlet>
<app-footer></app-footer>

navbar.component.html

...
 <button class="dropdown-item" (click)="showPopup()">LOGIN</button>
...

navbar.component.ts

import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';

@Component({
    moduleId: module.id,
    selector: 'app-nav-bar',
    templateUrl: 'navbar.component.html',
    styleUrls: ['./navbar.component.css'],
})

export class NavbarComponent implements OnInit {
    @Output() show = new EventEmitter<boolean>();

    ngOnInit() {
    }


    showPopup() {
        this.show.emit(true);
    }
}

login.component.html

<div id="wrapper-login-popup" class="fade-from-top" [(class.show)]="show">
    <div id="container-login-popup">
        <div class="row">
            <div class="col-sm-12 text-center">
                <img id="popup-bomb" src="assets/images/bomb.png" alt="bomb"/>
                <img id="popup-close" class="close-icon" src="assets/images/close.png" alt="close"
                     (click)="closePopup()"/>
            </div>
        </div>
   </div>
</div>

login.component.ts

import {Component, Input, OnInit} from '@angular/core';
import {AuthService} from '../services/auth.service';
import {IUser} from './user';

@Component({
    selector: 'app-login',
    templateUrl: 'login.component.html',
    styleUrls: ['login.css']
})

export class LoginComponent implements OnInit {
    private username: string;
    private password: string;

    @Input() show: boolean = false;

    constructor(private AuthService: AuthService) {
    }

    ngOnInit() {
    }

    login() {
        ...
    }

    showPopup() {
        console.log(this); //Show is false
        this.show = true;
        console.log(this); //Show is true but does not trigger the show class
    }

    closePopup() {
        this.show = false;
    }
}

Answer №1

The main issue at hand is that the nav-bar and login components are not able to directly communicate with each other since they are siblings. Even though you have set show as an output of navbar and as an input of login, there is no direct connection between them.

To resolve this, you should make updates in your app component to establish a connection between them.

export class AppComponent implements OnInit {
    show = false;
    onShow() { this.show = true; }
}

and within the template:

<app-nav-bar (show)="onShow()"></app-nav-bar>
<app-login [(show)]="show"></app-login>

While two-way binding can work for simple cases like this one, it is generally not recommended as it can lead to code that is difficult to maintain. It is advisable to designate one entity as the owner of the show variable and ensure all changes go through that entity. In this scenario, the app component would be the ideal owner. You may want to consider altering the login component to emit an event that modifies the show variable in the app component and remove any two-way bindings. For larger applications, having a separate service dedicated to managing pop-ups could be beneficial, eliminating the need to pass messages up and down the component tree.

Additionally, it is suggested to utilize ngClass for manipulating classes such as

[ngClass]="{'show': show}"

A service-based solution might involve

import {Subject} from 'rxjs/Subject';
@Injectable()
export class PopUpService {
    private showPopUpSource = new Subject();
    showPopUp$ = this.showPopUpSource.asObservable();
    showPopUp() { this.popUpSource.next(true); }
    closePopUp() { this.popUpSource.next(false); }
}

Ensure to provide this service at the app module or app component level:

providers: [PopUpService]

Make sure not to re-provide the service later, as you want a single instance shared among all components. Inject the service into both components and use its methods to show or close the popup.

In the login component, bind to the popUp$ observable like

constructor(private popUpSvc: PopUpService) {}
show$;
ngOnInit() { this.show$ = this.popUpSvc.showPopUp$; }
showPopUp() { this.popUpSvc.showPopUp(); }
closePopUp() { this.popUpSvc.closePopUp(); }

Subscribe in the template using the async pipe like

<div id="wrapper-login-popup" class="fade-from-top" [ngClass]="{'show': (show$ | async) }">

Utilizing the async pipe simplifies garbage collection management. Without it, you would need to manually handle garbage collection in ngOnDestroy by calling unsubscribe(), otherwise, subscriptions will accumulate. Furthermore, the async pipe triggers change detection, which becomes crucial when implementing onPush change detection for performance optimization.

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

Ember JS: Master of Controlling

I am working with the following controllers: clusters_controller.js.coffee Portal.DashboardClustersController = Ember.ArrayController.extend dashboard_controller.js.coffee Portal.DashboardController = Ember.ArrayController.extend In my template, I am ...

The HTML Bootstrap collapse feature is not functioning correctly upon the initial button press

Could you assist me with implementing a bootstrap and javascript collapse feature? I have two collapsible cards. The first card should be visible initially, but then switch to the second card when clicked on a link. However, upon the first click, both card ...

Prevent any further dissemination if I continue typing repeatedly

As I develop a custom search engine for a website, the search functionality operates through AJAX on keyup events. This means that when you begin typing (assuming you type more than two characters and after any leading or trailing spaces are removed), an A ...

Assess html code for Strings that include <% %> tags along with embedded scripts

Having a small issue with my code where I am receiving an HTML response from a web service as a java String. I need to display this String as HTML on my webpage, but the problem is that there are some script tags in the form of <% ... %> which are sh ...

Unordered calling of functions in JavaScript - is it possible?

I'm currently working on a project that involves extracting data from an SQL database and converting the output of a query (which is a number) into a corresponding color, which is then passed to a JavaScript variable. Essentially, I am using ajax to ...

Adjust the button's hue upon clicking it

The current function is operational. Upon pressing the button, it toggles between displaying "click me" and "click me again". However, I desire the button to appear blue when showing "click me" and red when displaying "click me again". <!DOCTYPE html ...

Designing a platform for dynamic components in react-native - the ultimate wrapper for all elements

export interface IWEProps { accessibilityLabel: string; onPress?: ((status: string | undefined) => void) | undefined; localePrefix: string; children: JSX.Element[]; style: IWEStyle; type?: string; } class WrappingElement extends React.Pure ...

Issues arising from TypeScript error regarding the absence of a property on an object

Having a STEPS_CONFIG object that contains various steps with different properties, including defaultValues, I encountered an issue while trying to access the defaultValues property from the currentStep object in TypeScript. The error message indicated tha ...

The sendKeys() method is malfunctioning in Selenium WebDriver

When attempting to enter phone numbers into the designated field, an error is being encountered. Exception in thread "main" org.openqa.selenium.NoSuchElementException: no such element The following code snippet is involved: driver.get("https://m ...

Installing external Javascript libraries on Parse cloud code can be done by following these steps

Currently, I have integrated these JavaScript libraries into my Parse cloud code. var request = require('request'); var fs = require('fs'); var Twit = require('twit'); However, the code refuses to compile if these libraries ...

The Jquery ajax request fails to function properly once the webpage has been published live

We've implemented a basic jQuery ajax call. A "more" button triggers a call to another PHP file when clicked. While it worked fine on my local server and initially on the live server, it suddenly stopped working live. The code functions perfectly o ...

How can a server retrieve a file uploaded using FormData and Ajax on a cross-domain upload?

my website is running on localhost:8084 and I need to upload a file to localhost:8086. Below is the JavaScript code: var xhr = new XMLHttpRequest(); xhr.open("post", "http://localshot:8086"+ "?type=ajax",true); xhr.setRequestHeader("X-Reque ...

Fetching Date and Time from the Internet using PHP

While I understand that similar questions have been asked numerous times before, I have yet to find a solution that fits my specific needs. My question is regarding how to retrieve the current date and time from the internet rather than relying on the loc ...

"Counting the clicks on the filter button in React

I have: var RightPanel = React.createClass({ componentDidMount: function () { this.load(); }, load: function(){ }, render: function () { return ( <div> <div className="row"> ...

Preventing multiple event handlers from firing on an HTML element after a server response

I currently have a div block on my page: <div class='btn'>click here</div> <div class='dialogWindow'></div> along with some JavaScript containing a click handler: $('.btn').live('click', fu ...

show.bs.modal event does not trigger within a bootstrap 4 modal that is loaded externally

I am trying to implement a feature where a common modal dialog can be loaded into any page from an external file. While the functionality works, I am facing an issue where the show.bs.modal and hide.bs.modal events are not triggered when the modal markup i ...

Can you explain the process of sending an AJAX request and managing it on a web server coded in C?

Could someone please provide an example of an AJAX request that retrieves a global variable stored on a webserver written in C? I am unfamiliar with JQuery and AJAX, so I would appreciate any guidance on how to accomplish this task. Thank you in advance ...

Issue - The NgFor directive is designed to only bind to Iterables like Arrays

I am attempting to showcase an array as options in a dropdown menu, but I keep encountering the following error: ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to It ...

Issue with Swiper js "autoheight" not resizing correctly for the height of the first slide upon page initialization

Hey everyone, I'm encountering a problem with my reactapp and I'm not sure how to resolve it. I've spent a considerable amount of time searching on stackoverflow but haven't come across any helpful solutions. The issue is related to a ...

Incorporate new markers into Google maps without the need to constantly initialize the map

My current goal is to have the user input a latitude and longitude into a text box, and then display a marker on the map for that location without needing to reinitialize the map each time. To start, I have set up my map like this: <script type="text/ ...