Search code examples
angularangular-materialweb-component

Using CDK Overlay Container in ShadowDOM


I am attempting to use Angular Material components inside of a web component. Everything is working except for one edge case with the cdk-overlays. So far I have had success using an override of _createContainer() to append the overlay container to a div inside of the shadowroot so that the styles will apply. However if I hide the parent element with an *ngIf and then toggle it back the overlay container is present in the DOM but it and its contained elements are not visible.

Is there something I'm missing where I need to manually clear or toggle something to get that behavior to work correctly?

Code is based off of this answer: Add MatDialog popup to Angular Root and not to body

@Injectable({ providedIn: "root" })
export class AppOverlayContainer extends OverlayContainer implements OnDestroy {
  constructor(@Inject(DOCUMENT) _document: any) {
    super(_document);
  }

  ngOnDestroy() {
    super.ngOnDestroy();
  }

  getRootElement(): Element {
    return this._document
      .querySelector("web-component-tag")
      .shadowRoot.querySelector("#angular-app-root");
  }

  createContainer(): void {
    this._createContainer();
  }

  protected _createContainer(): void {
    super._createContainer();
    this._appendToRootComponent();
  }

  private _appendToRootComponent(): void {
    if (!this._containerElement) {
     return;
    }
    const rootElement = this.getRootElement();
    const parent = rootElement || this._document.body;
    parent.appendChild(this._containerElement);
  }
}

HTML

<ng-container *ngIf="toggleWidget$ | async">


<app-conversations
    id="angular-app-root"
    [toggleWidget$]="toggleWidget$"
    [contextTitle]="contexttitle"
    [contexts]="contexts"
    [preloadConversations]="subscribeonload"
    [widgetIdentifier]="uniqueId"
  ></app-conversations>
</ng-container>
<ng-container
  *ngIf="(toggleWidget$ | async) === false && !overridelaunchbutton"
>
  <button
    mat-fab
    class="message-button"
    color="primary"
    (click)="toggleWidget$.next(true)"
  >
    <mat-icon svgIcon="message"></mat-icon>
    <div class="unread-pip" *ngIf="hasUnreadMessages">
      {{ unreadMessageCount }}
    </div>
  </button>
</ng-container>
<ng-container *ngIf="overridelaunchbutton">
  <button
    mat-icon-button
    [ngClass]="launchbuttonclass"
    [color]="launchbuttoncolor"
    (click)="toggleWidget$.next(true)"
  >
    <mat-icon svgIcon="{{ launchbuttonicon }}"></mat-icon>
    <div class="unread-pip" *ngIf="hasUnreadMessages">
      {{ unreadMessageCount }}
    </div>
  </button>
</ng-container>

Solution

  • I never found root to this beyond the weird behavior occurs if the overlay container is wrapped by an *ngIf. When the *ngIf is shown the first time, toggled to not be visible and then displayed again it causes an issue with the content being rendered inside the container. Moving it outside of the *ngIf fixed the issue in my case.