Angular CDK understanding the overlay position system
Asked Answered
T

4

31

I'm really trying to understand the overlay position parameter but without any luck. I also can't find any documentation about this topic.

What does the following code mean?

const positions = [
  new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }),
  new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' })
];
this.positionStrategy = this._overlay.position()
.flexibleConnectedTo(this.getConnectedElement())
.withPositions(positions)
.withFlexibleDimensions(false)
.withPush(false);
Toneme answered 20/9, 2018 at 19:59 Comment(0)
D
76

There still isn't much documentation about the Angular Overlay CDK. Most of what I've learned has come from their Github repo.

Global Position Strategy

This would be a global positioning strategy. The overlay you are creating would be positioned directly on the screen, not in relation to an element. This is good for a dialog popup or modal window.

  overlayConfig = this.overlay.position().global()
    .centerHorizontally().centerVertically();

Flexible Connected To Strategy

This is what you want to use for Toolbars, menu, things that pop out of a button. You'll have to pass in a reference to your button you want the overlay to be connected to:

<button id="toolbar-icons" cdkOverlayOrigin mat-button class="toolbar-button" (click)="this.showAppContext()">

and in your Component.ts

showAppContext() {
  const appOverlayRef: AppOverlayRef = 
    this.appOverlayService.open(this.origin);
}

ConnectionPositionPair - this is a list of preferred positions, from most to least desirable. So it will first try to use the first position you pass in.

originX: This will be 'start', 'end', or 'center'. It is the attachment point for your overlay. If you have passed in a button to your .flexibleConnectedTo function, this refers to the start, end and center of that element.

originY: this will be 'top', 'bottom' or 'center'. This refers to the top, bottom or center of the element passed in.

So { originX: 'start', originY: 'bottom' } would be the bottom left hand corner of the button.

overlayX and overlayY have the same options, but refer to the where the overlay will be attached to. { overlayX: 'start', overlayY: 'top' } is attaching the upper left hand corner of the overlay to the origin we have specified.

Then, as you can see in the array, we can pass multiple positions in. If the overlay doesn't fit in the first position, it will try the next position in the array. So, if the overlay doesn't fit on the screen the first way, it will automatically shift to the second position, which is defined here as connecting the upper-left hand of the bottom to the bottom left hand of the overlay.

const positions = [
  new ConnectionPositionPair(
   { originX: 'start', originY: 'bottom' },
   { overlayX: 'start', overlayY: 'top' }),
  new ConnectionPositionPair(
  { originX: 'start', originY: 'top' },
  { overlayX: 'start', overlayY: 'bottom' })];
];

withPush

You can set withPush to true, which will push the overlay on-screen if none of the provided positions fit.

The code is still the best place to see the documentation: https://github.com/angular/material2/blob/master/src/cdk/overlay/position/connected-position.ts

Degas answered 17/10, 2018 at 16:28 Comment(5)
Can you explain { originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }, please?Toneme
Added an explanation. Let me know if that helps, or if anything needs clarification.Degas
As of 2019 the Angular CDK docs might be useful: material.angular.io/cdk/overlay/overviewIsleana
It seems like such a powerful position system, yet I can't figure out how to align the right edge of my overlay to the left edge of the connected element. I am attempting to replicate bootstrap popover position="left"Pollack
This post should be in the documentation.Sarajane
J
14

David Rinck's answer to this question helped me after days and days of trial and error, so I thought I'd post the cheat sheet I put together based on that in the hope that it might help someone in the future.

This might not work for everybody, but it helped me:

// top-left
originX: 'start', // left corner of the button
originY: 'bottom', // bottom corner of the button
overlayX: 'start', // left corner of the overlay to the origin
overlayY: 'top', // top corner of the overlay to the origin

// top-right
originX: 'end', // right corner of the button
originY: 'bottom', // bottom corner of the button
overlayX: 'end', // right corner of the overlay to the origin
overlayY: 'top', // top corner of the overlay to the origin

// bottom-left
originX: 'start', // left corner of the button
originY: 'top', // top corner of the button
overlayX: 'start', // left corner of the overlay to the origin
overlayY: 'bottom', // top corner of the overlay to the origin

// bottom-right
originX: 'end', // right corner of the button
originY: 'top', // top corner of the button
overlayX: 'end', // right corner of the overlay to the origin
overlayY: 'bottom', // top corner of the overlay to the origin
Jankey answered 3/11, 2020 at 13:19 Comment(0)
I
6

David Rinck did a great job answering, what ended up working for me was using the template directives directly and not even getting into typescript overlay references ( overlay.postition / manually attaching/detaching etc.).

Here's an example where I create a modal that sits below a button I have. It was behaving oddly without setting positions explicitly, so I had to implement the positions array.

html:

<button cdkOverlayOrigin
        #trigger="cdkOverlayOrigin"
        (click)="isOpen = true">
  View Menu
</button>


<ng-template
    [cdkConnectedOverlayPanelClass]="'pm-responsive-text'"
    *ngIf="filterGroup?.filters?.length > numberOfVisibleFilters"
    cdkConnectedOverlay
    cdkConnectedOverlayPush ----- this pushes things on screen
    [cdkConnectedOverlayPositions]="positionPairs"
    [cdkConnectedOverlayOrigin]="filterTrigger"
    [cdkConnectedOverlayOpen]="isOpen"
    [cdkConnectedOverlayHasBackdrop]="true"
    (backdropClick)="isOpen = false">
    <div>...my modal code....</div>
</ng-template>

ts:

 // from my testing it doesn't seem to want to switch positions when 
 // resizing so I went ahead and just set it to the position I wanted and 
 // relied on cdkConnectedOverlayPush to handle the rest
positionPairs: ConnectionPositionPair[] = [
    {
      offsetX: 0,
      offsetY: 165,
      originX: 'start',
      originY: 'bottom',
      overlayX: 'start',
      overlayY: 'bottom',
      panelClass: null,
    },
  ];
Infelicity answered 4/5, 2022 at 18:43 Comment(0)
B
1

Just for the record, this is how I implemented a tooltip using the OverlayModule from Angular CDK

My component:

...
import { ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay';
...

@Component({
  selector: 'app-my-component-panel',
  standalone: true,
  imports: [
    OverlayModule
  ],
  templateUrl: './code-panel.component.html',
  ...
})
export class MyComponent {


  isCopied = false;

  topIsOpen = false;

  bottomIsOpen = false;

  leftOpen = false;

  rightIsOpen = false;

  // Tooltip Positions:

  topCenter: ConnectionPositionPair = {
    offsetY: -8,
    originX: 'center',
    originY: 'top',
    overlayX: 'center',
    overlayY: 'bottom',
  };

  bottomCenter: ConnectionPositionPair = {
    offsetY: 8,
    originX: 'center',
    originY: 'bottom',
    overlayX: 'center',
    overlayY: 'top',
  };

  leftCenter: ConnectionPositionPair = {
    offsetX: 8,
    originX: 'end',
    originY: 'center',
    overlayX: 'start',
    overlayY: 'center',
  };

  rightCenter: ConnectionPositionPair = {
    offsetX: -8,
    originX: 'start',
    originY: 'center',
    overlayX: 'end',
    overlayY: 'center',
  };
}

My template:

<div class="container">
  <button type="button"
          cdkOverlayOrigin
          #top="cdkOverlayOrigin"
          (mouseenter)="topIsOpen = true"
          (mouseleave)="topIsOpen = false">Tooltip on top</button>

  <button type="button"
          cdkOverlayOrigin
          (mouseenter)="bottomIsOpen = true"
          (mouseleave)="bottomIsOpen = false"
          #bottom="cdkOverlayOrigin">Tooltip on the bottom</button>

  <button type="button"
          (mouseenter)="leftOpen = true"
          (mouseleave)="leftOpen = false"
          cdkOverlayOrigin
          #left="cdkOverlayOrigin">Tooltip on the left</button>

  <button type="button"
          cdkOverlayOrigin
          (mouseenter)="rightIsOpen = true"
          (mouseleave)="rightIsOpen = false"
          #right="cdkOverlayOrigin">Tooltip on the right</button>
</div>


<!-- TOOLTIP TEMPLATES -->

<ng-template cdkConnectedOverlay
     [cdkConnectedOverlayOrigin]="top"
     [cdkConnectedOverlayOpen]="topIsOpen"
     [cdkConnectedOverlayPositions]="[topCenter, bottomCenter]" // FYI: if the isn't any available buffer on the top then display tooltip on the bottom
     >
  <div role="tooltip" class="tooltip">
    On top
  </div>
</ng-template>

<ng-template cdkConnectedOverlay
             [cdkConnectedOverlayOrigin]="bottom"
             [cdkConnectedOverlayOpen]="bottomIsOpen"
             [cdkConnectedOverlayPositions]="[bottomCenter, topCenter]" // FYI: if the isn't any available buffer on the bottom then display tooltip on the top
>
  <div role="tooltip" class="tooltip">
    On the bottom
  </div>
</ng-template>

<ng-template cdkConnectedOverlay
             [cdkConnectedOverlayOrigin]="left"
             [cdkConnectedOverlayOpen]="leftOpen"
             cdkConnectedOverlayGrowAfterOpen="true"
             [cdkConnectedOverlayPositions]="[rightCenter, leftCenter]" // FYI: if the isn't any available buffer on the left then display tooltip on the right
>
  <div role="tooltip" class="tooltip">
    On the left
  </div>
</ng-template>

<ng-template cdkConnectedOverlay
             [cdkConnectedOverlayOrigin]="right"
             [cdkConnectedOverlayOpen]="rightIsOpen"
             [cdkConnectedOverlayPositions]="[leftCenter, rightCenter]"> // FYI: if the isn't any available buffer on the right then display tooltip on the left
  <div role="tooltip" class="tooltip">
    On the right
  </div>
</ng-template>
Byelaw answered 26/4 at 12:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.