1

I wonder if anyone has ever solved this problem!

I am currently developing an AdministrationComponent (which is something like a singleton for CRUD operations), which is meant to manage the data on 4 different tables by sending the operations via HTTP request (encapsulated in a service) to the back-end, which then the results are sent to the client via WebSockets (preparing for a future where multiple clients are on this component simultaneously).

  • I am retrieving the data with HTTP requests to the backend, which I have no problem showing it in each of the tables.

  • I am instantiating Material Dialogs which encapsulate each of the CRUD operation for each of the tables. These, when validated and completed, send the correspondent HTTPRequest to the backend with the operation and its data. (Each table has its own service)

  • Then, I have a separate service that implements WebSockets and this is the one who updates the data displayed on the tables (because this AdministrationComponent will be used by more than one client).

I have to have to do validation(maybe not called validation(?)) that depends on data present in more than one table. For instance: let's say we have table A, B and C. One item from A can be related to items from C, but It can only be accessible if both A and B have a property in common. Since this type of operations is present in more than one table, I am planning on keeping this kind-of-a singleton component architecture, but please prove me wrong.

now, the problem: since showing the data on the tables is working just fine, my problem is with the sorting, filtering and the pagination of each table.

There seems to be no error on the code, but it simply only works for the first table and not for the other ones, as I cannot figure out how to reference the matSort and matPaginator correctly. Initialy, I was doing the ViewChild for the matSort and matPaginator like:

@Component({
  selector: 'app-administration',
  templateUrl: './administration.component.html',
  styleUrls: ['./administration.component.scss']
})
export class AdministrationComponent implements OnInit {
    users: any = [];  
    userTable_displayedColumns: string[] = ['name', 'organization', 'updatedAt', 'userActionsColumn'];
    userTable_dataSource: MatTableDataSource<any>;
    @ViewChild(MatPaginator, { static: true }) userTable_paginator: MatPaginator;
    @ViewChild(MatSort, { static: true }) userTable_sort: MatSort;

    userTable_applyFilter(filterValue: string): void {
        this.userTable_dataSource.filter = filterValue.trim().toLowerCase();
        if (this.userTable_dataSource.paginator) {
          this.userTable_dataSource.paginator.firstPage();
        }
      }

    constructor (/* service Injection here */) {

        this.userTable_dataSource = new MatTableDataSource;
        // initialization of table with Promised HTTP request
        // initialization of websockets channel which updates the data according to the backend

        // this is done for the 4 different tables

    }

    ngOnInit(): void {
        this.userTable_dataSource.paginator = this.userTable_paginator;
        this.userTable_dataSource.sort = this.userTable_sort;
        // this is done for the 4 different tables
    }

    // and the rest of the code here is filtering on the different tables, 
    //as well as launching Material Dialogs which are working fine
}

and the template (each table is in its own tab-pane):

    <!-- Users -->
    <div class="tab-pane active" id="link1">

      <div class="row">
        <div class="col-md">
          <button class="btn btn-enging btn-fill btn-wd mat-raised-button" mat-raised-button="" type="submit"
            (click)="openDialogCreateUser()">
            <span class="mat-button-wrapper">
              <i class="material-icons">person_add</i> Adicionar Utilizador
            </span>
            <div class="mat-button-ripple mat-ripple" matripple=""></div>
            <div class="mat-button-focus-overlay"></div>
          </button>
        </div>
        <div class="col-md-6">
          <mat-form-field class="example-full-width">
            <input matInput placeholder="Encontrar..." (keyup)="userTable_applyFilter($event.target.value)"
              value="">
          </mat-form-field>
        </div>
      </div>
      <div class="mat-elevation-z8">
        <table mat-table [dataSource]="userTable_dataSource" matSort>
          <tr mat-header-row *matHeaderRowDef="userTable_displayedColumns"></tr>
          <ng-container matColumnDef="userType">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Tipo de User </th>
            <td mat-cell *matCellDef="let row"> {{row.userType}} </td>
          </ng-container>
          <ng-container matColumnDef="username">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Username </th>
            <td mat-cell *matCellDef="let row"> {{row.username}} </td>
          </ng-container>
          <ng-container matColumnDef="name">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Nome </th>
            <td mat-cell *matCellDef="let row"> {{row.name}} </td>
          </ng-container>
          <ng-container matColumnDef="email">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Email </th>
            <td mat-cell *matCellDef="let row"> {{row.email}} </td>
          </ng-container>
          <ng-container matColumnDef="organization">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Organização </th>
            <td mat-cell *matCellDef="let row"> {{getOrganizationNameByID(row.organization)}} </td>
          </ng-container>
          <ng-container matColumnDef="updatedAt">
            <th mat-header-cell *matHeaderCellDef mat-sort-header> Último login </th>
            <td mat-cell *matCellDef="let row"> {{row.updatedAt | date:'HH:mm, dd/MM/y' }} </td>
          </ng-container>
          <ng-container matColumnDef="userActionsColumn">
            <th mat-header-cell *matHeaderCellDef mat-sort-header style="white-space: nowrap;"> Acções </th>
            <td mat-cell *matCellDef="let row">
              <div class="row">
                <span class="mat-button-wrapper" (click)="openDialogViewUser(row)" style="cursor: pointer">
                  <i class="material-icons" style="color:#163b65">remove_red_eye</i>
                </span>
                <span class="mat-button-wrapper" (click)="openDialogEditUser(row)" style="cursor: pointer">
                  <i class="material-icons" style="color:rgba(22, 59, 101, 1)">edit</i>
                </span>
                <span class="mat-button-wrapper" (click)="openDialogRemoveUser(row)" style="cursor: pointer">
                  <i class="material-icons" style="color:rgba(22, 59, 101, 1)">clear</i>
                </span>
              </div>
            </td>
          </ng-container>
          <tr mat-row *matRowDef="let row; columns: userTable_displayedColumns;"></tr>
        </table>
        <mat-paginator [pageSizeOptions]="[10, 25, 100]"></mat-paginator>
      </div>

    </div>
    <!-- Organizations, and so on ... -->

But then, I figured out how to use ViewChild properly(I guess) and tried: (referencing: https://stackoverflow.com/a/49015542/8919846 )

@ViewChild('userTablePaginator', { static: true }) userTable_paginator: MatPaginator;
@ViewChild('userTableSort', { static: true }) userTable_sort: MatSort;

template:

<table mat-table [dataSource]="userTable_dataSource" #userTableSort="matSort" matSort>
<mat-paginator #userTablePaginator="matSort" [pageSizeOptions]="[10, 25, 100]"></mat-paginator>

but it does not work.

Anyone have a clue how I can make it work? :)

3
  • Can you provide Stackblitz? Commented Nov 24, 2019 at 13:15
  • not realy, it's company's architecture. have you ever had some similar problem ? Commented Nov 24, 2019 at 13:52
  • There is lot of going on here. Its much easier if you post minimal problem. Commented Nov 24, 2019 at 13:56

1 Answer 1

1

I think I see your problem. Let's start with the ViewChild.

This will bring you the first paginator and assign it to userTable_paginator. If you have other paginators, then you won't be able to access them and bind the proper table paginator like you do in the first one:

this.userTable_dataSource.paginator = this.userTable_paginator;

So, what can you do. Use a different ViewChild for each paginator with in template reference.

<mat-paginator #userPaginator [pageSizeOptions]="[10, 25, 100]"></mat-paginator>

and in TS

@ViewChild('userPaginator', { static: true }) userTable_paginator: MatPaginator;

You can also use ViewChildren to get them all in a QueryList and work it from there (especially if you have some dynamic adding/removing. The ViewChildren will be updated when you add/remove MatPaginator.

Sign up to request clarification or add additional context in comments.

5 Comments

about the sorting, would the template be like <table mat-table [dataSource]="userTable_dataSource" #userTableSort matSort>, and the ViewChild @ViewChild('userTableSort', { static: true }) userTable_sort: MatSort; ?
You need to get the table and access the mat sort from there: stackoverflow.com/questions/48405373/…
I see. thanks for sharing! Although i've tried that approach before, I'm gonna test it again. I will only be able to do it tomorrow, so I'll let you know how it works out!
and about having multiple tables on the same component, do you have any advice?
I don't see why not. So long as you hide/unhide with ngIf so that your dom will not be overloaded with data and have performance issues. Though it would be best if you have one component per table and show/hide ad hoc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.