9

Is there a way to change the OverlayContainer?

I have created a tooltip component, but sometimes I want to attach the overlay to a specific element (by default the overlay is attached to the document body).

Here is how I am creating the overlay:

  private initOverlay(): void {
    const positionStrategy = this.overlayPositionBuilder
      .flexibleConnectedTo(this.elementRef)
      .withPositions([this.resolvedConfig]);

    this.overlayRef = this.overlay.create({positionStrategy});
  }

And this is how I am attaching a template to it:

  show(): void {
    this.overlayRef.attach(new TemplatePortal(this.tpl, this.viewContainerRef));
  }
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
vlio20
  • 8,955
  • 18
  • 95
  • 180

1 Answers1

13

Please reference this stackblitz example.

looks like you can accomplish this by extending the OverlayContainer class via the following in app.module.ts

{ provide: OverlayContainer, useFactory: () => new AppOverlayContainer() }

Stackblitz

https://stackblitz.com/edit/angular-material2-issue-ansnt5?file=app%2Fapp.module.ts


This GitHub comment also provides an example of how to package this in a directive

GitHub comment

https://github.com/angular/material2/issues/7349#issuecomment-337513040


Revision 3/22/19 working directive example

Extend the OverlayContainer class via cdk-overlay-container.ts

Stub the class in app.module.ts

  providers: [
    { provide: OverlayContainer, useClass: CdkOverlayContainer },
  ]

In your cdk-overlay-container.ts you are preventing the default _createContainer() from working, and providing your own custom public method myCreateContainer to replace it.

You are essentially creating an empty div here, adding a custom class to it my-custom-overlay-container-class and appending it to the div the directive is attached to, then passing that container to the private variable _containerElement in the true OverlayContainer class.

/**
* Create overlay container and append to ElementRef from directive
*/ 
public myCreateContainer(element: HTMLElement): void {
   let container = document.createElement('div');
   container.classList.add('my-custom-overlay-container-class');

   element.appendChild(container);
   this._containerElement = container;
 }
 /**
  * Prevent creation of the HTML element, use custom method above
  */
 protected _createContainer(): void {
     return;
 }

Then in your cdk-overlay-container.directive.ts your are calling myCreateContainer() and passing the ElementRef as an argument.

 this.cdkOverlayContainer['myCreateContainer'](this.elementReference.nativeElement);

Then in your HTML assign the directive where you want it to show up.

<div myCdkOverlayContainer 

Stackblitz

https://stackblitz.com/edit/angular-material2-issue-6nzwws?embed=1&file=app/app.component.html

Marshal
  • 10,499
  • 2
  • 34
  • 53
  • Yes, I saw this suggestion, the problem with this is that I might need to provide the custom OverlayContainer per each instance of the tooltip directive. Seems though there is no other way to achieve this. Would be nice if it would be configurable though. – vlio20 Mar 21 '19 at 21:20
  • Although I believe the directive approach may be a solution here, I am having trouble implementing it in stackblitz. Will update answer if I can get it going. – Marshal Mar 21 '19 at 22:28
  • Please see revision with directive example. – Marshal Mar 22 '19 at 15:50
  • 1
    In the implementation with `myCdkOverlayContainer` directive, all tooltips are attached to the last overlay container (4th). I am missing something here? I thought that is possible to attach different tooltips to different containers. Thanks. – andreivictor Apr 11 '20 at 08:38
  • 1
    The implementation is creating a container on each element to calculate the position of the tooltip. But ultimately, because the `OverlayContainer` class is a singleton, the tooltips will be rendered in the last container created. This example was to control the placement of the tooltip/calculate position. – Marshal Apr 13 '20 at 17:51
  • 1
    I get this error `TypeError: this.cdkOverlayContainer.myCreateContainer is not a function`. I followed your stackblitz example. Any ideas? – gyozo kudor Aug 25 '20 at 06:20
  • @gyozokudor I had this problem, it's caused by the directive getting an `OverlayContainer` instance rather than your custom class. It was solved by making my custom `OverlayContainer` class `@Injectable()`. – Chris Peacock Jan 08 '21 at 17:32