0

I'm trying to use Angular Material tables to list some infos I get from a backend server using websocket but it only shows the element when I click on the displayedColumns for sorting and not on initial load,

Here is my code for data table component:

import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTable } from '@angular/material/table';
import { observable, of as observableOf } from 'rxjs';
import { Observable } from 'rxjs/internal/Observable';
import { FtpClientComponent } from '../ftp-client/ftp-client.component';
import { FtpClientWsService } from '../services/ftp/ftp-client-ws.service';
import { DataTableDataSource, DataTableItem } from './data-table-datasource';

@Component({
  selector: 'app-data-table',
  templateUrl: './data-table.component.html',
  styleUrls: ['./data-table.component.css']
})
export class DataTableComponent implements AfterViewInit{
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;
  @ViewChild(MatTable) table!: MatTable<DataTableItem>;
  dataSource: DataTableDataSource;






  /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */


  displayedColumns = ['sftpName', 'sftpCreationDate'];

  constructor(data:FtpClientComponent) {
    this.dataSource =  new DataTableDataSource(data);

  }




  ngAfterViewInit(): void {
    this.dataSource.sort = this.sort;
    this.dataSource.paginator = this.paginator;
    this.table.dataSource = this.dataSource;
  }
}
 

here is the datasource typescript:

import { DataSource } from '@angular/cdk/collections';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { map } from 'rxjs/operators';
import { Observable, of as observableOf, merge } from 'rxjs';
import { FtpClientComponent } from '../ftp-client/ftp-client.component';

// TODO: Replace this with your own data model type
export interface DataTableItem {
  sftpName: string;
  sftpCreationDate: string;
  sftpPassword: string;
  sftpUsedSpace: string;
  sftpAllocatedSpace: string;
  sftpLastModification: string;
}

// TODO: replace this with real data from your application
// const EXAMPLE_DATA: any[] = [];

/**
 * Data source for the DataTable view. This class should
 * encapsulate all logic for fetching and manipulating the displayed data
 * (including sorting, pagination, and filtering).
 */
export class DataTableDataSource extends DataSource<DataTableItem> {
  data: any[];
  paginator: MatPaginator | undefined;
  sort: MatSort | undefined;

  constructor(data:FtpClientComponent) {
    super();
    this.data = data.receivedSftps

  }

  /**
   * Connect this data source to the table. The table will only update when
   * the returned stream emits new items.
   * @returns A stream of the items to be rendered.
   */
  connect(): Observable<DataTableItem[]> {
    if (this.paginator && this.sort) {
      // Combine everything that affects the rendered data into one update
      // stream for the data-table to consume.
      return merge(observableOf(this.data), this.paginator.page, this.sort.sortChange)
        .pipe(map(() => {
          return this.getPagedData(this.getSortedData([...this.data ]));
        }));
    } else {
      throw Error('Please set the paginator and sort on the data source before connecting.');
    }
  }

  /**
   *  Called when the table is being destroyed. Use this function, to clean up
   * any open connections or free any held resources that were set up during connect.
   */
  disconnect(): void {}

  /**
   * Paginate the data (client-side). If you're using server-side pagination,
   * this would be replaced by requesting the appropriate data from the server.
   */
  private getPagedData(data: DataTableItem[]): DataTableItem[] {
    if (this.paginator) {
      const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
      return data.splice(startIndex, this.paginator.pageSize);
    } else {
      return data;
    }
  }

  /**
   * Sort the data (client-side). If you're using server-side sorting,
   * this would be replaced by requesting the appropriate data from the server.
   */
  private getSortedData(data: DataTableItem[]): DataTableItem[] {
    if (!this.sort || !this.sort.active || this.sort.direction === '') {
      return data;
    }

    return data.sort((a, b) => {
      const isAsc = this.sort?.direction === 'asc';
      switch (this.sort?.active) {
        case 'sftpName': return compare(a.sftpName, b.sftpName, isAsc);
        case 'sftpCreationDate': return compare(+a.sftpName, +b.sftpName, isAsc);
        default: return 0;
      }
    });
  }
}

/** Simple sort comparator for example ID/Name columns (for client-side sorting). */
function compare(a: string | number, b: string | number, isAsc: boolean): number {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}

 

And the ftp-client component just uses a service that opens a websocket connection to retreive data that are then stored in a list

import { Component, OnInit } from '@angular/core';
import { FtpClientWsService } from '../services/ftp/ftp-client-ws.service';

@Component({
  selector: 'app-ftp-client',
  templateUrl: './ftp-client.component.html',
  styleUrls: ['./ftp-client.component.css']
})
export class FtpClientComponent implements OnInit {

  constructor(
    public SftpWsService: FtpClientWsService
  ) { }

  ngOnInit(): void {
    this.SftpWsService.openWebSocket();
  }


  receivedSftps = this.SftpWsService.sftps;


}
 

The list appears only when I click on the sort headers, I'm new to Angular so any feedbacks are very welcome

3
  • In your DataTableComponent class can you try moving your property assignments from ngAfterViewInit() into ngOnInit()? When the data bound properties are set, the OnInit hook is called before the AfterView hook. Commented Jul 16, 2021 at 1:38
  • I get an error this.table is undefined I guess it's because @ViewChild(MatTable) table!: MatTable<DataTableItem>; is not initialized yet
    – Mousse
    Commented Jul 16, 2021 at 9:04
  • waiting for a better way to do I added a dummy function that waits for 200ms and clicks on the first HTMLElement of MatSort, it works but is an ugly thing to do...
    – Mousse
    Commented Jul 16, 2021 at 10:27

2 Answers 2

1

adding a subcription on ngOninit() to the Subject in service before setting the datasource value solved the issue

0

I followed one of the above trick and worked for me as well, I share you the code below.

Note:Please do update me if any other better approach is found.

In HTML (adder click event on sort element named justrun)

In ts justrun(){ setTimeout(() => {

}, 2); }

Called the above function in constructor this.justrun();

Hope that you find it relevant.

1
  • You can use codeblocks to style the code in a more readable fashion Commented Sep 2, 2022 at 9:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.