Components: Overlay/Dialog with custom OverlayContainer

Created on 17 May 2017  路  8Comments  路  Source: angular/components

Bug, feature request, or proposal:

Feature request / proposal

What is the use-case or motivation for changing an existing behavior?

Put an overlay over a component where the backdrop would only cover a custom OverlayContainer element.

Basically I was trying to get the functionality Panel from angular material 1:
https://material.angularjs.org/1.1.4/demo/panel

Looking at the source of Overlay seems to be the basic building block which already provides much of the functionality needed I saw that the container where the Overlay is appended to the DOM is provided as an injectable.

Overlay:
https://github.com/angular/material2/blob/master/src/lib/core/overlay/overlay.ts

OverlayContainer:
https://github.com/angular/material2/blob/master/src/lib/core/overlay/overlay-container.ts

Are there already any plans or guidelines on how this kind of architecture can/should be used to provide a custom OverlayContainer?

P5 cdoverlay feature

Most helpful comment

I've published an implementation and a little demo of the functionality.

https://github.com/reppners/ngx-cdk-dynamic-overlay-container

Maybe it can help other people that need similar functionality.

All 8 comments

@reppners does providing a custom OverlayContainer not give you what you need to do this?

Here is a plunkr
http://plnkr.co/edit/859v6jFIcFoHGnjVwE0u?p=preview

There is a component as dialog content and a custom overlay container which is provided as OverlayContainer.

I expected to see the console log when the custom overlay container is instantiated but it does not appear.

Is the way of overriding the OverlayContainer in main.ts wrong?

EDIT: scratch that.. silly me had a console filter active.. I'll continue to work on that plunkr to show my use case.

http://plnkr.co/edit/859v6jFIcFoHGnjVwE0u?p=preview

Got it to work in a basic way. There is a css hack involved to make the overlay's position: fixed relative to a nested DOM element. The custom parent element needs -webkit-transform: translateZ(0); set.

However in my real application I'm trying to overload the provider in some nested component in the providers section of the Component decorator. The component is inside a lazy loaded module. In that context it does not even run the constructor of the custom OverlayContainer.

To summarize taking the approach of using the hierarchical DI seems to work for this simple use-case.
What about a nested lazy-loaded component that wants to show some overlay appended to an element from its template for e.g. a card details view but also wants to show top-level dialogs as alerts? Would an indirection over a service defined in a core module use the original OverlayContainer when opening a dialog?

EDIT:
Fiddled with it in the real application and had some findings:

  • Providing the custom OverlayContainer does only work on a NgModule
  • To make a certain component make use of it it must provide Overlay in the component which will result in a fresh service instance using the custom OverlayContainer implementation.

The problem with this is that due to hierarchical DI every other component which is in this part of the tree will also use the custom OverlayContainer so its not possible to have a component open an Overlay in a special location in the DOM and another component a global Overlay/Dialog. Maybe if the App Injector instance could be injected to make sure its the root instance of a service but this also seems rather unobvious.

Somehow this feels like a dead end but maybe I'm also not aware of an obvious solution to this.

Could the Overlay/Dialog api support a parameter to provide an ElementRef that is used as the OverlayContainer parent element?

EDIT2:
Another solution:
Provide a new instance of Overlay / MdDialog / YourServiceUsingOverlay with a separate, distinct InjectionToken. This would clearly separate the services using the global OverlayContainer instance from any special service instances.

@reppners are you able to get what you need by creating your own custom OverlayModule? E.g.

@NgModule({
    providers: [
    Overlay,
    OverlayPositionBuilder,
    VIEWPORT_RULER_PROVIDER,
    PizzaOverlayContainer,
  ]
})
export class PizzaOverlayModule { }

And then using that custom module in place of the built-in OverlayModule everywhere?

I've published an implementation and a little demo of the functionality.

https://github.com/reppners/ngx-cdk-dynamic-overlay-container

Maybe it can help other people that need similar functionality.

@reppners
Thanks for sharing your implementation Stefan - thorough and useful.
Do you know perhaps, since you create the overlay dynamically, if it's possible to tap onto the dialog config and define a start position (top and left in px) for the dialog - so it would animate-in from the trigger element (button)?

This functionality was provided in Material v1, but is missing from Material +v2 (this thread discusses it)

It is possible to do this when customizing the method explained here, and is even more useful when decoupling the component from the service, as explained here. But your method is elegant and succinct, so it would be great to add the dialog positioning to it.

@AsafAgranat Unfortunately I don't have a solution at hand and also don't have the time to explore the possibilities to find one.

I've published an implementation and a little demo of the functionality.

https://github.com/reppners/ngx-cdk-dynamic-overlay-container

Maybe it can help other people that need similar functionality.

Hey... this example is not working angular 7.
I am getting below error.
Cannot read property 'createElement' of null
at DynamicOverlay.push../node_modules/@angular/cdk/esm5/overlay.es5.js.Overlay._createHostElement (overlay.es5.js:4137)
at DynamicOverlay.push../node_modules/@angular/cdk/esm5/overlay.es5.js.Overlay.create (overlay.es5.js:4066)
at DynamicMatDialog.push../node_modules/@angular/material/esm5/dialog.es5.js.MatDialog._createOverlay (dialog.es5.js:924)
at DynamicMatDialog.push../node_modules/@angular/material/esm5/dialog.es5.js.MatDialog.open (dialog.es5.js:846)
at NetworkMonitorDetailsComponent.push../src/app/site-manager/network-monitor/components/network-monitor-details/network-monitor-details.component.ts.NetworkMonitorDetailsComponent.pingIPAddress (network-monitor-details.component.ts:140)
at Object.eval [as handleEvent] (NetworkMonitorDetailsComponent.html:10)
at handleEvent (core.js:23107)
at callWithDebugContext (core.js:24177)
at Object.debugHandleEvent [as handleEvent] (core.js:23904)
at dispatchEvent (core.js:20556)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

constantinlucian picture constantinlucian  路  3Comments

theunreal picture theunreal  路  3Comments

Miiekeee picture Miiekeee  路  3Comments

xtianus79 picture xtianus79  路  3Comments

savaryt picture savaryt  路  3Comments