How to put mat-sort-header in menu

1.3k Views Asked by At

can anyone tell me how to extract the mat-sort-header functionality from the headers into a menu. I want to open a menu when I click on one of the header. Within this menu I want to use the matSort functionality.

In fact, I want to trigger the matSortChange event

1

There are 1 best solutions below

0
Eliseo On

You're asking about "contextual menu" and "mannage mat-sort". It's a broad question and really I don't know how answer, so a brief data

If you see the source of matSort. You can "override" the method sort.

You can use some like

@ViewChild(MatSort) sort: MatSort;

ngAfterViewInit(){
    this.sort.sort=(sortable: MatSortable)=>{
      //this only to update the variables active and direction
    
      if (this.sort.active != sortable.id) {
        this.sort.active = sortable.id;
        this.sort.direction = sortable.start ? sortable.start : this.sort.start;
      } else {
        this.sort.direction = this.sort.getNextSortDirection(sortable);
      }
      
      //this make the table sorted
      this.sort.sortChange.emit({active: this.sort.active, direction: this.sort.direction});
    }

    this.dataSource.sort = this.sort;
}

So you can choose "not emit the change" or emit in a contextual menu.

For contextual menu you can use cdk-overlay. It's not well documented, but if you want an example, sometime ago I wrote this SO.

Another option is that each td of your header was a mat-menu

NOTE: I don't know how many is your knowledge in Angular, so it's possible this answer don't help you so much. Give us more data

Update well, we has all necesary with this data, so go on!

First we "override" the sort function to do nothing and create a new function to make the sort

  ngOnInit()
  {
    this.sort.sort=(sortable: MatSortable)=>null
    this.dataSource.sort=this.sort
  }
  sortTable(active:string,direction:string)
  {
    this.sort.active=active
    this.sort.direction=direction as SortDirection
    this.sort.sortChange.emit({active: active, direction: direction as SortDirection});
  }

Then use mat-menu in the th. So our th will be like

  <ng-container matColumnDef="position">
    <th mat-header-cell *matHeaderCellDef (click)="positionTrigger.openMenu()">
      <span mat-button [matMenuTriggerFor]="position" #positionTrigger="matMenuTrigger">No.</span>
      <mat-menu #position="matMenu">
        <button mat-menu-item (click)="sortTable('position','asc')">
          <mat-icon
            *ngIf="sort.active=='position' && sort.direction=='asc'"
            >checked</mat-icon
          ><span>Ascend</span>
        </button>
        <button mat-menu-item (click)="sortTable('position','desc')">
          <mat-icon
            *ngIf="sort.active=='position' && sort.direction=='desc'"
            >checked</mat-icon
          ><span>Descend</span>
        </button>
      </mat-menu>
    </th>
    <td mat-cell *matCellDef="let element">{{element.position}}</td>
  </ng-container>

Finally we need some .css to "remove" the arrow that material add. In styles.css

.custom-table .mat-sort-header-arrow
{
  visibility:collapse;
  min-width: 0;
}

And to beauty our menu in component.css

button.mat-menu-item
{
  
  height: 1.75rem!important;
  font-size:12px;
  text-overflow: unset;
  display:flex;
  align-items: center;
  justify-content: flex-start;
}

button.mat-menu-item >span
{
  padding-right: 24px;
  padding-left:24px;
}
button.mat-menu-item .mat-icon+span
{
  padding-left:0;
}

.mat-menu-item .mat-icon
{
  font-size:12px;
  height:1.75rem!important;
  margin-right: 0;
  margin-top:1rem;

}

the result in this stackblitz