Displaying a mat-select inside a modal dialog

2019-09-03 03:37发布

问题:

I am displaying a mat-select inside a ngx-smart-dialog, when I open the select it shows behind the dialog. I found this and this with this solution:

.cdk-global-overlay-wrapper, .cdk-overlay-container {
    z-index: 9999!important;
}

That's solves the issue if I put it in the global .scss file. What I want to know is (because in what I read I didn't find an explanation) why putting it inside the component's view (i.e., the modal component's view) it doesn't work.

Thanks in advance!

回答1:

By default, angular scopes a component's CSS / SCSS to a component using a special attribute selector that Angular automatically creates. This means that component styles only affect DOM elements in that component's view. Put another way, the component's styles only affect DOM elements which are children of the component.

mat-select renders the overlay pane using the CDK overlay package (which I think uses a CDK portal itself). The CDK overlay package renders the mat-select's overlay pane outside of your Angular application's root component (appended to the document body). This means that, despite mat-select being inside your component's template, and despite the mat-select element being a child element of your component in the DOM, the mat-select's overlay pane is NOT a child element of your component in the DOM. This means that your component's styling will not touch the overlay pane (or any other elements which are not children of your component).

I've run into this issue before myself. Personally, I consider this behavior to be a bug in Angular's emulated css scoping functionality. However, the angular team is aware of the issue and views it as an acceptable limitation of their implementation. I think I remember seeing a comment from one of the Angular maintainers that they currently don't know of a way of fixing this issue in a performant manner (so I don't think this will ever change). Similarly, native shadow DOM encapsulation ONLY allows a component's CSS to affect children of a component, so this causes the same issues when rendering overlays (I believe the spec made this decision for performance reasons as well).

This all being said, there are two ways you can place the css in your component's css file css file and still make things work (instead of needed to place the css in a "global" css file).

  1. Disable css scoping for the component using the @Component({encapsulation: ViewEncapsulation.None}) option. This disables css scoping for the component making all of the component's css "global". Unlike normal "global" css, a component's css is added and removed from the DOM as a component is created / destroyed (so the component's css will only be in the DOM if the component is).
    • If choosing this option, you can still manually scope individual styles to the component by using the component's element selector.
  2. Set a component's css scoping to ViewEncapsulation.Emulated (which is the default) and use the angular custom ::ng-deep pseudo selector to selectively remove scoping from certain css styles in the component's css file.

    Example: ::ng-deep { .cdk-global-overlay-wrapper, .cdk-overlay-container { z-index: 9999!important; } }

    • The ::ng-deep selector is deprecated in angular, but the angular team has no current plans to remove the selector and they still recommend you use it, if needed, for the time being.
    • ViewEncapsulation.Native and ViewEncapsulation.ShadowDom don't support the ::ng-deep selector (and don't have any support for piercing selectors).