Overlay

The overlay service provides an easy and quick way to dynamically render content in the foreground of an app. The content to be rendered, also the way it renders (e.g. placement, animations, scroll and click behaviors) are highly configurable and able to match all of the possible scenarios. The overlay service is fully integrated in the toggle directive.

Demo

Getting Started

First we need to import the IgxOverlayService in the component and inject a reference to it in the component's constructor:


import { Inject } from '@angular/core'
import { IgxOverlayService } from `igniteui-angular`;

...

export class MyOverlayComponent {
    constructor(
        @Inject(IgxOverlayService) private overlayService: IgxOverlayService
    )
}

...

Displaying Content

The overlay service can be used to dynamically display an HTMLNode or even an Angular Component by attaching it to the overlay DOM.

After a reference to the Overlay service is established, it can be used to dynamically show/hide content. For example, we can pass an Angular Component in the attach method. This will generate a unique ID, which we can pass to the show method to display the component:


// my-overlay-component.component.ts
import { MyDynamicComponent } from '../my-dynamic-component/my-dynamic-component.component';

export class MyOverlayComponent {
    private _overlayId = ''; // The unique identifier assigned to the component by the Overlay service
    ... 
    // a reference to the OverlayService is defined via @Inject in the constructor
    // under this.overlayService

    public showInOverlay() {
        if (!this._overlayId) {
            this._overlayId = this.overlayService.attach(MyDynamicComponent);
        }
        this.overlayService.show(this._overlayId);
    }
}
<!-- my-overlay-component.component.html -->
<div class='content'>
...
    <button (click)="showInOverlay()">Show Overlay</button>
</div>

If we want to pass an already existing ElementRef from the page to the IgxOverlayService, we can do it as follows:

<!-- my-overlay-component.component.html -->
<div class='content'>
    <button (click)="showInOverlay()">Show Overlay</button>
</div>
<div>
    <img #exampleImage width='200px' src='../assets/example.png' title='Click Me!'>
</div>
// my-overlay-component.component.ts
import { Inject, ViewChild } from '@angular/core'
export class MyOverlayComponent {
    private _overlayId = ''; // The unique identifier assigned to the component by the Overlay service

    @ViewChild('exampleImage', {read: ElementRef})
    private exampleImage: ElementRef;
    public showInOverlay() {
        if (!this._overlayId) {
            this._overlayId = this.overlayService.attach(this.exampleImage);
        }
        this.overlayService.show(this._overlayId);
    }
}

The Overlay Service's attach() method has two overloads:

  • attach(element, settings?)
  • attach(component, settings?, moduleRef?)

The first parameter in both overloads is mandatory and represents the content that will be shown in the overlay. There are a couple of different scenarios how the content can be passed:

  • A component definition - When passing a component in as the first argument, the overlay service creates a new instance of that component and dynamically attaches it to the overlay DOM. If moduleRef is provided the service will use the module's ComponentFactoryResolver and Injector when creating the ComponentRef instead of the root ones.
  • An ElementRef to an existing DOM element (illustrated in the sample above) - Any view that is already rendered on the page can be passed through the overlay service and be rendered in the overlay DOM. Using this approach when show(id) is called, overlay will:
    • Get the reference to the passed view from Angular
    • Detach the view from the DOM and leave an anchor in its place
    • Re-attach the view to the overlay, using the show() method settings or falling back to the default overlay settings
    • On close, it will re-attach the view back to it's original location in the DOM

Attaching Components

In the below demo, we can pass the IgxCard component through the Overlay Service's attach() method to generate an ID. Then we call the show() method with the provided ID to dynamically attach the card to the DOM in a modal container.

Overlay Settings

The attach() method also accepts an object of the OverlaySettings type, which configures the way the content is shown. If no such object is provided, the Overlay Service will use its default settings to render the passed content.

For example, if we want the content to be positioned relative to an element, we can pass a different positioningStrategy to the attach() method, e.g. ConnectedPositioningStrategy. In order to configure how the component is shown, we need to create an OverlaySettings object first:

// my-overlay-component.component.ts
// import the ConnectedPositioningStategy class
import { ConnectedPositioningStrategy } from 'igniteui-angular';
...
export class MyOverlayComponent {

    @ViewChild(`myAnchorButton`)
    private myAnchorButton: ElementRef;
    private _overlayId = ''; // The unique identifier assigned to the component by the Overlay service

    public showInOverlay() {
        if (!this._overlayId) {
            this._overlayId = this.overlayService.attach(MyDynamicComponent, {
                positionStrategy: new ConnectedPositioningStrategy({
                    target: this.myAnchorButton.nativeElement
                })
            });
        }
        this.overlayService.show(this._overlayId);
    }
}
<!-- my-overlay-component.component.html -->
<div class='content'>
...
<button #myAnchorButton (click)="showInOverlay()">Show Overlay</button>
</div>

Clicking on the button will now show MyDynamicComponent positioned relative to the button.

Hiding the Overlay

The IgxOverlayService.hide() method removes the content from the overlay and, if applicable, re-attaches it to it's original location in the DOM.

All of the elements rendered by the overlay service have a unique ID, assigned to them by the service. The IgxOverlayService.attach() method returns the identifier of the rendered content. To remove content from the overlay, that ID needs to be passed to the overlay's hide() method.

We can modify the previously defined overlay method to not only show but also hide the overlay element

// my-overlay-component.component.ts
// add an import for the definion of ConnectedPositioningStategy class
import { ConnectedPositioningStrategy } from 'igniteui-angular';
...
export class MyOverlayComponent {
    private _overlayId = ''; // The unique identifier assigned to the component by the Overlay service
    private _overlayShown = false; // Is the component rendered in the Overlay?

    @ViewChild(`myAnchorButton`)
    private myAnchorButton: ElementRef;

    public toggleOverlay() {
        if (!this._overlayShown) { // If the element is not visible, show it
            //  generate ID
            if (!this._overlayId) {
                this._overlayId = this.overlayService.attach(MyDynamicComponent, {
                    positionStrategy: new ConnectedPositioningStrategy({
                        target: this.myAnchorButton.nativeElement,
                        closeOnOutsideClick: false, // overlay will not close on outside clicks
                        modal: false // overlay content will not be rendered in a modal dialog
                    }) // The attach method returns an ID that can be used to reference the shown content
                });
            }

            this.overlayService.show(this._overlayId);
        } else { // If the element is visible, hide it
            this.overlayService.hide(this._overlayId); // Find and remove the component from the overlay container
        }
        this._overlayShown = !this._overlayShown;
    }
}
<!-- my-overlay-component.component.html -->
<div class='content'>
...
    <button #myAnchorButton (click)="toggleOverlay()">Toggle Overlay</button>
</div>

Attaching Settings

Using the overlaySettings parameter of the attach() method, we can change how the content is shown - e.g. where the content is positioned, how the scroll should behave and if the container is modal or not

If no overlaySettings are configured, the toggled element gets the default display settings:

defaultOverlaySettings = {
    positionStrategy: new GlobalPositionStrategy(),
    scrollStrategy: new NoOpScrollStrategy(),
    modal: true,
    closeOnOutsideClick: true
};

Integration with igxToggle

The IgxToggleDirective is fully integrated with the IgxOverlayService. As such, the Toggle Directive's toggle() method allows for custom overlay settings to be passed when toggling the content.

An example of how to pass configuration settings to the toggle's method is shown below:

<!-- In example.component.html -->
<div>
    <button igxToggle (click)="callToggle()">Click me!</button>
    <div [style.visibility]="collapsed ? 'hidden ' : 'visible'">
        This content is toggle-able!
    </div>
</div>
// example.component.ts
@Component({
    selector: `example-component`,
    template: `example.component.html`
})
export class ExampleComponent {
    @ViewChild(IgxToggleDirective)
    private toggleDirective: IgxToggleDirective;

    public get collapsed(): boolean {
        return this.toggleDirective.collapsed;
    }

    public callToggle(): void {
        const overlaySettings: OverlaySettings = {
            positionStrategy: new AutoPositionStrategy(),
            scrollStrategy: new BlockScrollStrategy(),
            modal: true,
            closeOnOutsideClick: false
        }
        this.toggleDirective.toggle(overlaySettings)
    }
}

Assumptions and Limitations

If you show the overlay in an outlet, and if the outlet is a child of an element with transform, perspective or filter set in the CSS you won't be able to show the modal overlay. The reason for this is if one of the above mentioned CSS properties is set, the browser creates a new containing block and the overlay is limited to this containing block, as described here.

API References

Additional Resources