Angular2, multi-functional overlay element that can be integrated with all components throughout the application

These are the two components I have:

overlay

@Component({
    selector: 'overlay',
    template: '<div class="check"><ng-content></ng-content></div>'
})
export class Overlay {
    save(params) {
        //bunch of stuff that are commonly used
    }
}

myComponent (and there are plenty more like this)

@Component({
    selector: 'myComponent',
    directives: [Overlay],
    template: '<overlay><form (ngSubmit)="save({ name: 'sam', lastName: 'jones' })">I'm the component. Click <input type="submit">Here</input> to save me!</form></overlay>'
})
export class MyComponent {

}

This approach doesn't seem to be working as expected, with Angular 2 skipping initialization of both components without any error messages. However, the idea here is to have a common component that can wrap multiple other components in a generic way. Since it involves a complex layout and functionality, using a service may not suffice. Perhaps exploring custom annotations could help achieve such behavior? Any suggestions on how to implement this functionality?

Please note: The overlay component contains essential logic and template structures necessary for any other component utilizing it. The template includes a sophisticated modal dialog with animations, messages, fades, etc., that are consistent throughout the application.

Update

Came across this resource: Angular2: progress/loading overlay directive

Answer №1

For this specific task, I developed a custom overlay component instead of using Bootstrap modal due to the need for distinct window modals and progress modals on components.

Overlay

@Component({
    selector: 'overlay',
    template:
    `<div [ngClass]="isOpen ? 'opened' : 'closed'">
         <div class="modal" role="dialog">
            <div class="modalBody">
                <div *ngIf="isSaving">
                    <span class="text-success text-bold">
                        Saving...
                    </span>
                </div>
                <div *ngIf="isSaved">
                    <span class="text-success text-bold">
                        Saved.
                    </span>
                </div>
            </div>
        </div>
    </div>`,
    styles: [
        '.modal { position:absolute; width: 100%; height: 100%; margin: -30px; background-color: rgba(255, 255, 255, 0.7); z-index: 1000; text-align: center; }',
        '.closed { visibility: hidden; }',
        '.opened { visibility: visible; }',
        '.modalBody { top: 45%; left: 25%; width: 50%; position: absolute; }',
        '.text-bold { font-weight: 800; font-size: 1.5em; }'
    ]
})
export class Overlay implements OnChanges, AfterContentInit {
    @Input() isSaving: boolean = false;
    @Input() isSaved: boolean = false;
    @Input() containerElement: HTMLElement;

    isOpen = false;

    private modalElement;

    constructor(private element: ElementRef, private animationBuilder: AnimationBuilder) { }

    ngOnChanges() {
        if (this.modalElement) {
            if (this.isSaving == true || this.isSaved == true) {
                this.toggleAnimation(true);
            }
            else if (this.isSaving == false && this.isSaved == false) {
                this.toggleAnimation(false);
            }
        }
    }

    ngAfterContentInit() {
        this.containerElement.style.position = 'relative';
        this.modalElement = this.element.nativeElement.querySelector('.modal');
    }

    private toggleAnimation(isOpen) {
        var startCss = { backgroundColor: 'rgba(255, 255, 255, 0)' };
        var endCss = { backgroundColor: 'rgba(255, 255, 255, 0.7)' };

        if (isOpen) {
            this.isOpen = true

            this.animation(
                true,
                this.modalElement,
                400,
                startCss,
                endCss,
                null
            );
        }
        else {
            this.animation(
                isOpen,
                this.modalElement,
                400,
                startCss,
                endCss,
                () => {
                    this.isOpen = false;
                }
            );
        }
    }

    private animation(isStart, element, duration, startCss, endCss, finishedCallback) {
        var animation = this.animationBuilder.css();

        animation.setDuration(duration);

        if (isStart) {
            animation.setFromStyles(startCss).setToStyles(endCss);
        } else {
            animation.setFromStyles(endCss).setToStyles(startCss)
        }

        animation.start(element);

        if (finishedCallback) {
            setTimeout(finishedCallback, duration);
        }
    }
}

Usage

In its current state, the overlay component requires a relative container for proper display. The CSS may need adjustments for mobile devices and non-positioned containers. Here's how it's currently implemented:

HTML

<form action="/edit" method="post" #myForm="ngForm" (ngSubmit)="save ajax method that will update the isSaving and isSaved accordingly" novalidate>
    <div style="position: relative;" #overlayContainer>
        <overlay [isSaving]="isSaving" [isSaved]="isSaved" [containerElement]="overlayContainer"></overlay>
    </div>
</form>

After submitting the form, the overlay appears within the specified containerElement for 400ms, then fades out and hides until the next save operation. Handling the isSaving and isSaved values is the responsibility of the parent component utilizing the overlay.

Answer №2

While this may not directly address your question, it seems like you are interested in incorporating modals into your app based on your comment. I have yet to implement this feature, but it is a requirement for my app in the near future.

Personally, I don't use bootstrap javascript since I am already using Angular. All you really need is the bootstrap css.

Take a look at the root template below:

 <my-app></my-app>

 <my-modal>
    <div class="modal-container">
       <div class="modal-content">{{modalContent}}</div>
    </div>
 </my-modal>

Show Your Modal: Global Event

You can display your modal from any component. The key is to use a modal-service to trigger a global event. Check out this answer to understand how it functions.

The event object can contain any information needed by your modal, such as its content or a specific key for the modal component to retrieve elsewhere.

ModalComponent

Your ModalComponent should listen for this event. Upon receiving the event, you can show or hide the modal component using various techniques that you may be familiar with. Using structural directives is one approach.

Style

A modal typically consists of a container (.modal-container) with dimensions matching the viewport and a transparent background. Inside this, the modal's content (.modal-content) is housed within another container with fixed dimensions and an absolute position. If desired, you can incorporate bootstrap styles and leverage angular animations for additional flair.

I have successfully employed a similar technique for a menubar and found it to work quite effectively!

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

Calculate the date and time three months before or after a specified date

I have the following start date : 2023-09-03T00:00:00+05:30 and end date : 2023-09-10T00:00:00+05:30 My objective is to deduct 90 days from the start date and add 90 days to the end date Afterwards, I need to convert it to UTC format In order to achieve ...

When working with Angular 5, the question arises: how and where to handle type conversion between form field values (typically strings) and model properties (such

As a newcomer to Angular, I am struggling with converting types between form field values (which are always strings) and typed model properties. In the following component, my goal is to double a number inputted by the user. The result will be displayed i ...

Having issues with Bootstrap customization in an Angular 7 project

I am currently working on customizing a Bootstrap 4 theme within an Angular 7 project. After installing Bootstrap, I updated my angular .json file to include the following: "styles": [ "./node_modules/@angular/material/prebuilt-themes/de ...

Navigate to a different component within Angular

Is there a way in Angular to scroll to a component using a button placed in another component? Below is the code snippet for the first component: <div id='banner' class="col-5 offset-1 d-flex justify-content-center align-items-cen ...

Trouble encountered with the implementation of setValue on placeholder

When I send the value for age, it is being treated as a date in the API that was built that way. However, when I use setValue to set the form value and submit the form, it also changes the placeholder text, which is not what I want. I would like the placeh ...

Angular 4, Trouble: Unable to resolve parameters for StateObservable: (?)

I've been working on writing unit tests for one of my services but keep encountering an error: "Can't resolve all parameters for StateObservable: (?)". As a result, my test is failing. Can someone please help me identify and fix the issue? Here& ...

I'm in need of someone who can listen and detect any changes in my notifications table (node) in order to perform real-time data

Seeking a listener in Firebase to track changes in my notifications table for real-time data monitoring. My project is utilizing Angular 2 with TypeScript and Firebase. ...

Can I exclusively utilize named exports in a NextJS project?

Heads up: This is not a repeat of the issue raised on The default export is not a React Component in page: "/" NextJS I'm specifically seeking help with named exports! I am aware that I could switch to using default exports. In my NextJS ap ...

Retrieving the Final Value from an Observable in Angular 8

Is there a way to retrieve the most recent value from an Observable in Angular 8? let test = new BehaviorSubject<any>(''); test.next(this.AddressObservable); let lastValue = test.subscribe(data=>console.log(data.value)); Despite my ef ...

How to Define Intersection Type with Symbol in TypeScript?

I'm currently working on a helper function that associates a Symbol with a value. function setCustomSymbol<S extends symbol, T>(symbol: S, data: T, defaultValue: any = true): S & T { /*...*/ } The issue I'm facing is trying to instruc ...

What is the best way to refresh an observable with updated information?

I am working with a singleton service that uses Observables to retrieve data from a server and display it: class HttpService { constructor() { this.$blocks = this.managerService .get() .pipe(shareReplay(1)); } } Within the templat ...

Ionic - InAppBrowser continuously redirects to external web browser instead of staying within the in-app browser

When I was testing my Ionic App on localhost:8100 page using ionic serve, the developer console showed a warning message: Native: InAppBrowser is not installed or you are running on a browser. Falling back to window.open. The same issue occurred when I ...

Dark Theme Issue with Angular Material's CheckBox in Mat-Menu

If you try to place a <mat-checkbox> inside a <mat-menu>, the dark themes won't apply to the text part of your <mat-checkbox>. Look at the image at the end for reference. A similar issue arises with <mat-label>s. However, the ...

The custom class-validator decorator in NestJS fails to retrieve the value from the parameter

In my Nestjs project, I have created a Custom ValidatorConstraint using class-validator. The purpose is to create my own decorator and apply it later on DTO classes for validations. Let's consider this route: foo/:client After a request is made, I w ...

Integrating router-outlet into Angular 2 component affects ngModel functionality

Currently, I am experimenting with angular 2 beta 9 and have encountered an issue that I would like some help with. In my component, I have bound an input field using the following code: [(ngModel)]="email" (ngModelChange)="changedExtraHandler($event)" ...

What is the proper type declaration for incoming data from the backend in my TypeScript code when using axios?

In the TypeScript code snippet provided, the type for 'e' (used in the function for form submission) has been figured out. However, a question arises if this type declaration is correct. Additionally, in the catch block, the type "any" is used fo ...

Issues with the functionality of Angular 5 EventEmitter

I have been trying to call a function from the Parent Component in the Child Component, and here is how I implemented it: project-form.component.ts @Component({ selector: 'app-project-form', templateUrl: './project-form.component.html& ...

Utilize Typescript/Javascript to utilize the Gmail API for sending emails via email

I am trying to send emails from my application using my Gmail account with Ionic. I have followed tutorials from SitePoint and Google Developers. Here is how I'm initializing the client: client_id: gapiKeys.client_id, discoveryDocs: ["https://www.goo ...

The possibility exists that the onClick function may be null

I am encountering an issue with a props function that is showing an error message stating that the object may be null. import {Dropdown} from "react-bootstrap"; interface GenreButtonProps { key: number; id: number | null; genre: strin ...

Creating and Injecting Singleton in Angular 2

I have a custom alert directive set up in my Angular app: import { Component } from 'angular2/core'; import { CORE_DIRECTIVES } from 'angular2/common'; import { Alert } from 'ng2-bootstrap/ng2-bootstrap'; @Component({ sele ...