Open mzernetsch opened 7 years ago
I think this should be possible by providing a custom overlay container, but I've also never tried it.
See https://github.com/angular/material2/blob/master/src/cdk/overlay/overlay.md#the-overlay-container
@mzernetsch here's an example of this working. I think the css could be cleaner, but it should get you started!
https://stackblitz.com/edit/angular-material2-issue-ansnt5?file=app%2Fapp.module.ts
Thanks for the example. This approach solves the problem with the snackbar but creates a new problem with the dialog component.
Dialogs and SnackBars use the same OverlayContainer, so when i open a dialog while using a custom OverlayContainer it will also be positioned within this custom container: https://stackblitz.com/edit/angular-material2-issue-qc4fq9
Maybe there could be a "DialogOverlayContainer" and a "SnackBarOverlayContainer" which both default to "OverlayContainer". A developer could then define a custom provider as needed for a specific component.
Hm yeah. I feel like there is a better answer than having all the specific providers (arguably also be needed for select, menu, etc), but my working DI knowledge isn't able to come up with anything.
@mzernetsch maybe https://github.com/angular/material2/issues/4612#issuecomment-340049922 could help!
I resolved it the way like https://github.com/angular/material2/issues/7764#issuecomment-337335591
Create the class AppOverlayContainer
(src/app-overlay-container.ts)
import { OverlayContainer } from '@angular/cdk/overlay';
export class AppOverlayContainer extends OverlayContainer {
constructor() {
super(undefined);
}
protected _createContainer(): void {
const container: HTMLDivElement = document.createElement('div');
container.classList.add('app-overlay-container');
const element: Element | null = document.querySelector('.mat-sidenav-container');
if (element !== null) {
element.appendChild(container);
this._containerElement = container;
}
}
}
Then add { provide: OverlayContainer, useFactory: (): AppOverlayContainer => new AppOverlayContainer() }
to your @NgModule
providers
This will place all snackbars below the toolbar and you can scroll in the .mat-sidenav-content
without loosing the snackbar
Last tested in Angular 6.1.2 and Material2 6.4.3
Edit 1: Use the following class in style.scss
.app-overlay-container {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 1000;
}
Has there been a proper fix for this implemented? I see the "viewContainerRef" but not sure about that, as it doesn't seem the intended result.
It's been noted elsewhere that the VCR determines where in the Angular component tree the overlay component lives, for purposes of change detection etc, but does not impact where in the DOM the overlay element gets inserted. IIRC the stated reason for this is that Snackbar is intended to always work at body
level, even in cases where the Angular app might coexist with non-Angular content and thus not occupy the entire viewport. I believe the intended solution is the use of a custom Overlay provider but I have yet to see a good example in the official docs.
I personally would like to see Snackbar updated to allow explicit specification of an Overlay instance just for use by the Snackbar service, or even an open
method option that says "show this Snackbar in this Overlay, not the default one", but I have no reason to think that's coming any time soon.
Please provide an official way of positioning the mat-snack-bar within a different container other than body.
Actually, going through the MatSnackBarConfig (https://material.angular.io/components/snack-bar/api#MatSnackBarConfig), we should have been able to pass a ViewContainerRef
for the overlay to attach to, but is not being used by SnackBar not Overlay. Tracing through the code in MatSnackBar's _createOverlay
it never even tried to do anything with the provided ViewContainerRef, and the Overlay didn't bother reading it there?
The positionStrategy is hardcoded to use global...
var positionStrategy = this._overlay.position().global();
The solution mentioned by @willshowell and @Shinigami92 will not work for dynamically created content... it banks on the fact that you have a way to finding your desire element based on id
or a unique element class in your application...
The spec does seem to show putting the snackbar over the content, but above app chrome like the the bottom nav and fab (https://material.io/components/snackbars/#placement), this may be a good argument for adding this feature
Bug, feature request, or proposal:
feature request
What is the expected behavior?
There should be a setting to define a container in which the snackbar is positioned in.
What is the current behavior?
SnackBar is always positioned based on the viewport.
What are the steps to reproduce?
https://stackblitz.com/edit/angular-material2-issue-ddtruw
What is the use-case or motivation for changing an existing behavior?
I have an app with a static main toolbar at the top. When i position the snackbar to "top right" it will be shown above the main toolbar (see stackblitz example). I would like it to be shown at the top right of the container below the main toolbar (".basic-container" in my stackblitz example).
Which versions of Angular, Material, OS, TypeScript, browsers are affected?
angular 4.4.2 angular-material 2.0.0 - Beta12 Chrome 61.0.3163.100
Is there anything else we should know?
MatSnackBarConfig has a viewContainerRef property. At first i thought this would be exactly what i need but when i tried to change it, it didn't seem to do anything. In the documentation of the MatDialog component (which also has a viewContainerRef property) it is stated that this property does not affect the place where it is rendered. I assume the same goes for MatSnackBar.