Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular - Open Bottom Sheet (MatBottomSheet) inside parent container

I'm trying to open a material bottom sheet inside a div container, as default it's opening as the last element inside the body.

Looking at the documentation, I need to use the viewContainerRef, but I can't make it work.

This is similiar to what I'm trying to do:

app.component.html:

...
<div #container></div>
...

app.component.ts:

export class AppComponent implements AfterViewInit {

    @ViewChild('container', { read: ViewContainerRef }) _container;

    ...

    constructor(
        private bottomSheet: MatBottomSheet,
    ) {}

    ngAfterViewInit() {
        this.bottomSheet.open(MySheetComponent, {
            panelClass: 'my-modal',
            viewContainerRef: this._container,
        });
    }
}

But it doesn't seems to make any change.

Any help would be appreciated.

Thanks in advance

like image 910
ET-CS Avatar asked Aug 12 '26 18:08

ET-CS


1 Answers

It seems that there is a misunderstanding of what viewContainerRef option really means.

The MatBottomsheet is created with ComponentPortal. From ComponentPortal documentation, we can found a more detailed description of viewContainerRef:

Where the attached component should live in Angular's logical component tree.

This is different from where the component renders,

which is determined by the PortalOutlet. The origin is necessary when the host is outside of the Angular application context.

When inspecting the DOM when a bottomsheet is opened, we'll find the following component hierarchy:

  1. OverlayContainer - <div class="cdk-overlay-container"></div>
  2. Overlay - <div id="cdk-overlay-{id}" class="cdk-overlay-pane">
  3. ComponentPortal - <bottom-sheet-container></bottom-sheet-container>
  4. Component - our BottomSheet.

Now, what we really need to change is the OverlayContainer, which, according to docs, is:

the container element in which all individual overlay elements are rendered.

By default, the overlay container is appended directly to the document body.

Technically it is possible to provide a custom overlay container:

@NgModule({
  providers: [{provide: OverlayContainer, useClass: CustomOverlayContainer}],
 // ...
})
export class MyModule { }

export class AppOverlayContainer extends OverlayContainer {

  protected _createContainer(): void {
    const container: HTMLDivElement = document.createElement('div');
    container.classList.add('app-overlay-container');

    const element: Element | null = document.querySelector('#custom-overlay-contaier');
    if (element !== null) {
      element.appendChild(container);
      this._containerElement = container;
    }
  }
}

but, unfortunately, it is a singleton and creates a new problem:

MatBottomSheet, MatDialog, MatSnackbar, MatTooltip components use the same OverlayContainer, so all these components will open within this custom container.

like image 147
andreivictor Avatar answered Aug 14 '26 12:08

andreivictor



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!