Search code examples
angularsortingangular-materialng-modal

Angular 11 - mat-sort is not working in ng-modal table


mat-sort and paginator is not working in ng-modal table. But outside modal it is working properly. Are there any workaround for this issue?


Solution

  • The problem is that, when you try asign the mat-sort, the mat-sort is not reachable -because really it's not displayed-. You need asign the mat-sort after displayed. If you're using mat-dialog, when you open use a setTimeout(). You can think setTimeout as you say to Angular: "After render, don't forget execute this instructions"

    const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
      width: '250px',
      data: {name: this.name, animal: this.animal}
    });
    setTimeout(()=>{ //<--here you say to Angular the sort
       this.dataSource.sort = this.sort;
    })
    

    If you're using ng-bootstrap modal

    open(content) {
        this.modalService.open(content, {ariaLabelledBy: 'modal-basic-title'}).result.then((result) => {
          this.closeResult = `Closed with: ${result}`;
        }, (reason) => {
          this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
        });
        setTimeout(()=>{ //<--here you say to Angular the sort
           this.dataSource.sort = this.sort;
        })
      }
    

    NOTE: remember that, as the mat-table is not visible all the time the mat-sort ViewChild should be like

    @ViewChild(MatSort) sort: MatSort;
    

    NOT use {static true} -static true it's used only if the element is always displayed in the component-

    Update really the solution proposed not work. Inside a template the "viewChild are not accesible. So the only I get is create a fool directive

    @Directive({
      selector: '[fool]',
      exportAs: 'fool'
    })
    export class FoolDirective implements OnInit {
      ngOnInit()
      {
        setTimeout(()=>{
          (this.table.dataSource as any).sort=this.sort
        })
      }
      constructor(@Optional() private sort:MatSort,@Optional() private table:MatTable<any>){}
    }
    

    So, we only need add this directive to our table

    <table mat-table [dataSource]="dataSource"
      matSort fool
      class="mat-elevation-z8">
     ....
    </table>
    

    See the [stackblitz][1]

    Update2 For add a paginator, the thing is more complex. My idea is improve our directive fool, adding a MatPaginator as Optional and send as output

    export class FoolDirective implements OnInit {
      @Output() paginatorLoaded:EventEmitter<MatPaginator>=new EventEmitter<MatPaginator>()
      ngOnInit()
      {
        setTimeout(()=>{
          if (this.table && this.sort)
            (this.table.dataSource as any).sort=this.sort
          if (this.paginator)
            this.paginatorLoaded.emit(this.paginator)
        })
      }
      constructor(@Optional() private sort:MatSort,@Optional() private table:MatTable<any>,@Optional() private paginator:MatPaginator){}
    }
    

    Then, the only is, when add the Paginator write

    <mat-paginator  fool 
             (paginatorLoaded)="dataSource.paginator=$event"
    ....>
    </mat-paginator>
    

    The only is change the .css adding in styles.css

    .cdk-overlay-container {
      z-index: 9000;
    }
    

    else the select of number of pages don't show -it is showed, but is under the popup

    NOTE: Really I don't like so much these work-around, perhaf is time to re-think and use another popup -e.g. the material- or use the ngb-bootstrap modal with components nor with template. [1]: https://stackblitz.com/edit/angular-hbadro?file=src%2Fapp%2Ffool.directive.ts