Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular CDK connect overlay with scroll to container

Tags:

(First post here, so bear with me)

I have a table inside a container where I display an icon for rows that meet certain criterias. Clicking the icon should open an overlay to display some information, and the overlay should stay open even if I scroll inside the container. The scroll initially followed the body of the page, so I started creating a custom strategy. However, I cannot get it to follow the table scroll. The overlay sticks in one place and does not move accordingly.

Overlay is generated as below. Any tips on how this is generally solved would be appreciated!

private createOverlay() {
  const positionStrategy = this.overlay.position()
    .flexibleConnectedTo(this.overlayorigin.elementRef)
    .withFlexibleDimensions(false)
    .withPush(false)
    .withPositions([
      {
        originX: 'start',
        originY: 'bottom',
        overlayX: 'start',
        overlayY: 'top'
      },
      {
        originX: 'start',
        originY: 'top',
        overlayX: 'start',
        overlayY: 'bottom',
       }
    ]);
    
  const overlayConfig = new OverlayConfig({
    hasBackdrop: false,
    scrollStrategy: this.overlay.scrollStrategies.reposition({autoClose: false}),
    positionStrategy: positionStrategy
  });
    
    
  this._overlayRef = this.overlay.create(overlayConfig);
  this._overlayRef.backdropClick().subscribe(_ => {
    this._overlayRef.detach();
    this.closeWarning.emit(true);
  });
    
  this._portal = new TemplatePortal(this.content, this.portal);
  this._overlayRef.attach(this._portal);
}
like image 428
Kris Avatar asked Nov 25 '18 20:11

Kris


1 Answers

I do not have your MVP but from code that you have shared, it seems to be working fine.

Take a look at this working stackblitz that I created using code that you shared and few pieces from material.angular.io.

import {
  Overlay,
  OverlayConfig,
  OverlayRef
} from "@angular/cdk/overlay";
import {
  TemplatePortal
} from "@angular/cdk/portal";
import {
  DataSource
} from "@angular/cdk/table";
import {
  Component,
  ElementRef,
  EventEmitter,
  Output,
  TemplateRef,
  ViewChild,
  ViewContainerRef
} from "@angular/core";
import {
  BehaviorSubject,
  Observable
} from "rxjs";

@Component({
  selector: "cdk-overlay-basic-example",
  templateUrl: "cdk-overlay-basic-example.html",
  styleUrls: ["cdk-overlay-basic-example.css"]
})
export class CdkOverlayBasicExample {
  @ViewChild("templatePortalContent")
  templatePortalContent: TemplateRef < unknown > ;

  @Output() closeWarning = new EventEmitter < boolean > ();

  isOpen = false;
  displayedColumns: string[] = [
    "action",
    "position",
    "name",
    "weight",
    "symbol"
  ];
  dataSource = new ExampleDataSource();

  private _overlayRef: OverlayRef;
  private _portal: TemplatePortal;

  constructor(
    private overlay: Overlay,
    private _viewContainerRef: ViewContainerRef
  ) {}

  click(overlayorigin: ElementRef, element: PeriodicElement) {
    console.log(arguments);
    element.isOpen = !element.isOpen;
    if (element.isOpen) {
      this.createOverlay(overlayorigin);
    } else {
      this._overlayRef.detach();
    }
  }

  private createOverlay(overlayorigin: ElementRef) {
    const positionStrategy = this.overlay
      .position()
      .flexibleConnectedTo(overlayorigin)
      .withFlexibleDimensions(false)
      .withPush(false)
      .withPositions([{
          originX: "start",
          originY: "bottom",
          overlayX: "start",
          overlayY: "top"
        },
        {
          originX: "start",
          originY: "top",
          overlayX: "start",
          overlayY: "bottom"
        }
      ]);

    const overlayConfig = new OverlayConfig({
      hasBackdrop: false,
      scrollStrategy: this.overlay.scrollStrategies.reposition({
        autoClose: false
      }),
      positionStrategy: positionStrategy
    });

    this._overlayRef = this.overlay.create(overlayConfig);
    this._overlayRef.backdropClick().subscribe(_ => {
      this._overlayRef.detach();
      this.closeWarning.emit(true);
    });

    this._portal = new TemplatePortal(
      this.templatePortalContent,
      this._viewContainerRef
    );
    this._overlayRef.attach(this._portal);
  }
}

export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
  isOpen ? : boolean;
}

const ELEMENT_DATA: PeriodicElement[] = [{
    position: 1,
    name: "Hydrogen",
    weight: 1.0079,
    symbol: "H"
  },
  {
    position: 2,
    name: "Helium",
    weight: 4.0026,
    symbol: "He"
  },
  {
    position: 3,
    name: "Lithium",
    weight: 6.941,
    symbol: "Li"
  },
  {
    position: 4,
    name: "Beryllium",
    weight: 9.0122,
    symbol: "Be"
  },
  {
    position: 5,
    name: "Boron",
    weight: 10.811,
    symbol: "B"
  },
  {
    position: 6,
    name: "Carbon",
    weight: 12.0107,
    symbol: "C"
  },
  {
    position: 7,
    name: "Nitrogen",
    weight: 14.0067,
    symbol: "N"
  },
  {
    position: 8,
    name: "Oxygen",
    weight: 15.9994,
    symbol: "O"
  },
  {
    position: 9,
    name: "Fluorine",
    weight: 18.9984,
    symbol: "F"
  },
  {
    position: 10,
    name: "Neon",
    weight: 20.1797,
    symbol: "Ne"
  },
  {
    position: 11,
    name: "Hydrogen",
    weight: 1.0079,
    symbol: "H"
  },
  {
    position: 12,
    name: "Helium",
    weight: 4.0026,
    symbol: "He"
  },
  {
    position: 13,
    name: "Lithium",
    weight: 6.941,
    symbol: "Li"
  },
  {
    position: 14,
    name: "Beryllium",
    weight: 9.0122,
    symbol: "Be"
  },
  {
    position: 15,
    name: "Boron",
    weight: 10.811,
    symbol: "B"
  },
  {
    position: 16,
    name: "Carbon",
    weight: 12.0107,
    symbol: "C"
  },
  {
    position: 17,
    name: "Nitrogen",
    weight: 14.0067,
    symbol: "N"
  },
  {
    position: 18,
    name: "Oxygen",
    weight: 15.9994,
    symbol: "O"
  },
  {
    position: 19,
    name: "Fluorine",
    weight: 18.9984,
    symbol: "F"
  },
  {
    position: 20,
    name: "Neon",
    weight: 20.1797,
    symbol: "Ne"
  },
  {
    position: 21,
    name: "Hydrogen",
    weight: 1.0079,
    symbol: "H"
  },
  {
    position: 22,
    name: "Helium",
    weight: 4.0026,
    symbol: "He"
  },
  {
    position: 23,
    name: "Lithium",
    weight: 6.941,
    symbol: "Li"
  },
  {
    position: 24,
    name: "Beryllium",
    weight: 9.0122,
    symbol: "Be"
  },
  {
    position: 25,
    name: "Boron",
    weight: 10.811,
    symbol: "B"
  },
  {
    position: 26,
    name: "Carbon",
    weight: 12.0107,
    symbol: "C"
  },
  {
    position: 27,
    name: "Nitrogen",
    weight: 14.0067,
    symbol: "N"
  },
  {
    position: 28,
    name: "Oxygen",
    weight: 15.9994,
    symbol: "O"
  },
  {
    position: 29,
    name: "Fluorine",
    weight: 18.9984,
    symbol: "F"
  },
  {
    position: 30,
    name: "Neon",
    weight: 20.1797,
    symbol: "Ne"
  }
];

export class ExampleDataSource extends DataSource < PeriodicElement > {
  data = new BehaviorSubject < PeriodicElement[] > (ELEMENT_DATA);

  connect(): Observable < PeriodicElement[] > {
    return this.data;
  }

  disconnect() {}
}
.container {
  height: 400px;
  overflow: auto;
  border: 1px solid grey;
  margin: 10px 0;
}

table {
  width: 100%;
  border-collapse: collapse;
}

td,
th {
  padding: 5px;
}

.content {
  margin: 5px 0;
  background: green;
  height: 1000px;
}

.overlay-info {
  padding: 20px;
  border: 1px solid #865e0b;
  background-color: orange;
  color: white;
  font-size: 16px;
}
<div class="container">
  <table cdk-table [dataSource]="dataSource" border="1">
    <ng-container cdkColumnDef="action">
      <th cdk-header-cell *cdkHeaderCellDef> Action </th>
      <td cdk-cell *cdkCellDef="let element">
        <button (click)="click(origin, element)" type="button" cdkOverlayOrigin #origin>
          {{element.isOpen ? "Close" : "Open"}}
        </button>
      </td>
    </ng-container>

    <ng-container cdkColumnDef="position">
      <th cdk-header-cell *cdkHeaderCellDef> No. </th>
      <td cdk-cell *cdkCellDef="let element"> {{element.position}} </td>
    </ng-container>

    <ng-container cdkColumnDef="name">
      <th cdk-header-cell *cdkHeaderCellDef> Name </th>
      <td cdk-cell *cdkCellDef="let element"> {{element.name}} </td>
    </ng-container>

    <ng-container cdkColumnDef="weight">
      <th cdk-header-cell *cdkHeaderCellDef> Weight </th>
      <td cdk-cell *cdkCellDef="let element"> {{element.weight}} </td>
    </ng-container>

    <ng-container cdkColumnDef="symbol">
      <th cdk-header-cell *cdkHeaderCellDef> Symbol </th>
      <td cdk-cell *cdkCellDef="let element"> {{element.symbol}} </td>
    </ng-container>

    <tr cdk-header-row *cdkHeaderRowDef="displayedColumns"></tr>
    <tr cdk-row *cdkRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>

<div class="container">
  <div class="content">
    This is a very very long content!
  </div>
</div>

<div class="container">
  <div class="content">
    This is another very very long content!
  </div>
</div>

<ng-template #templatePortalContent>
  <div class="overlay-info">
    This is information panel!
  </div>
</ng-template>
like image 88
Dipen Shah Avatar answered Nov 15 '22 05:11

Dipen Shah