I'm developing a WebApp with MEANStack, currently working on the Front End with Angular. My App simply reads SQL Data Tables and displays them on a browser in the form of Angular Tables.
Here's how it looks like: Data Table Display MEANStack App
Unfortunately the Sort functionality does not work. Any idea why? Furthermore, how can I implement the "scroll left and right" functionality, given that my tables are a bit large?
Here's my code (Please do not hesitate to ask for more code):
tables-list.component.html
<mat-spinner *ngIf="isLoading"></mat-spinner>
<h1 class="mat-body-2">Process List </h1>
<mat-accordion multi="true" *ngIf="userIsAuthenticated && !isLoading">
<mat-expansion-panel>
<mat-expansion-panel-header>
Process List
</mat-expansion-panel-header>
<table mat-table [dataSource]="processTables" matSort class="mat-elevation-z8" *ngIf="userIsAuthenticated">
<!-- ProcessID Column -->
<ng-container matColumnDef="ProcessID">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ProcessID </th>
<td mat-cell *matCellDef="let element"> {{element.ProcessID}} </td>
</ng-container>
<!-- ProcessName Column -->
<ng-container matColumnDef="ProcessName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ProcessName </th>
<td mat-cell *matCellDef="let element"> {{element.ProcessName}} </td>
</ng-container>
<!-- PackageVersion Column -->
<ng-container matColumnDef="PackageVersion">
<th mat-header-cell *matHeaderCellDef mat-sort-header> PackageVersion </th>
<td mat-cell *matCellDef="let element"> {{element.PackageVersion}} </td>
</ng-container>
<!-- RobotType Column -->
<ng-container matColumnDef="RobotType">
<th mat-header-cell *matHeaderCellDef mat-sort-header> RobotType </th>
<td mat-cell *matCellDef="let element"> {{element.RobotType}} </td>
</ng-container>
<!-- PackagePath Column -->
<ng-container matColumnDef="PackagePath">
<th mat-header-cell *matHeaderCellDef mat-sort-header> PackagePath </th>
<td mat-cell *matCellDef="let element"> {{element.PackagePath}} </td>
</ng-container>
<!-- CreationTime Column -->
<ng-container matColumnDef="CreationTime">
<th mat-header-cell *matHeaderCellDef mat-sort-header> CreationTime </th>
<td mat-cell *matCellDef="let element"> {{element.CreationTime}} </td>
</ng-container>
<!-- Status Column -->
<ng-container matColumnDef="Status">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
<td mat-cell *matCellDef="let element"> {{element.Status}} </td>
</ng-container>
<!-- Header and Row Declarations -->
<tr mat-header-row *matHeaderRowDef="displayedprocessTablesColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedprocessTablesColumns;"></tr>
</table>
</mat-expansion-panel>
</mat-accordion>
<br> <h1 class="mat-body-2">Applications List </h1>
<mat-accordion multi="true" *ngIf="userIsAuthenticated && !isLoading">
<mat-expansion-panel>
<mat-expansion-panel-header>
Applications List
</mat-expansion-panel-header>
<table mat-table [dataSource]="applicationsTables" matSort class="mat-elevation-z8" *ngIf="userIsAuthenticated">
<!-- ProcessName Column -->
<ng-container matColumnDef="ProcessName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ProcessName </th>
<td mat-cell *matCellDef="let element"> {{element.ProcessName}} </td>
</ng-container>
<!-- PackageVersion Column -->
<ng-container matColumnDef="PackageVersion">
<th mat-header-cell *matHeaderCellDef mat-sort-header> PackageVersion </th>
<td mat-cell *matCellDef="let element"> {{element.PackageVersion}} </td>
</ng-container>
<!-- WorkflowsBelongingToProcess Column -->
<ng-container matColumnDef="WorkflowsBelongingToProcess">
<th mat-header-cell *matHeaderCellDef mat-sort-header> WorkflowsBelongingToProcess </th>
<td mat-cell *matCellDef="let element"> {{element.WorkflowsBelongingToProcess}} </td>
</ng-container>
<!-- ApplicationsBelongingToWorkflow Column -->
<ng-container matColumnDef="ApplicationsBelongingToWorkflow">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ApplicationsBelongingToWorkflow </th>
<td mat-cell *matCellDef="let element"> {{element.ApplicationsBelongingToWorkflow}} </td>
</ng-container>
<!-- Header and Row Declarations -->
<tr mat-header-row *matHeaderRowDef="displayedApplicationsTablesColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedApplicationsTablesColumns;"></tr>
</table>
</mat-expansion-panel>
</mat-accordion>
tables.module.ts
import { NgModule } from "@angular/core";
import { ReactiveFormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";
import { RouterModule } from "@angular/router";
import { TableListComponent } from "./tables-list.component";
import { AngularMaterialModule } from "../angular-material.module";
@NgModule({
declarations: [TableListComponent],
imports: [
CommonModule,
ReactiveFormsModule,
AngularMaterialModule,
RouterModule
]
})
export class TablesModule {}
angular-material.module.ts
import { NgModule } from "@angular/core";
import {
MatInputModule,
MatCardModule,
MatButtonModule,
MatToolbarModule,
MatExpansionModule,
MatProgressSpinnerModule,
MatPaginatorModule,
MatDialogModule,
MatSelectModule,
MatListModule,
MatIconModule,
MatTableModule,
MatSortModule,
MatTabsModule,
MatAutocompleteModule,
MatBadgeModule,
MatBottomSheetModule,
MatButtonToggleModule,
MatCheckboxModule,
MatChipsModule,
MatStepperModule,
MatDatepickerModule,
MatGridListModule,
MatDividerModule,
MatMenuModule,
MatNativeDateModule,
MatProgressBarModule,
MatRadioModule,
MatRippleModule,
} from "@angular/material";
import { CdkTableModule } from "@angular/cdk/table";
import { CdkTreeModule } from "@angular/cdk/tree";
@NgModule({
exports: [
MatInputModule,
MatCardModule,
MatButtonModule,
MatToolbarModule,
MatExpansionModule,
MatProgressSpinnerModule,
MatPaginatorModule,
MatDialogModule,
MatSelectModule,
MatListModule,
MatIconModule,
MatTableModule,
MatSortModule,
MatTabsModule,
CdkTableModule,
CdkTreeModule,
MatAutocompleteModule,
MatBadgeModule,
MatBottomSheetModule,
MatButtonToggleModule,
MatCheckboxModule,
MatChipsModule,
MatStepperModule,
MatDatepickerModule,
MatDividerModule,
MatGridListModule,
MatMenuModule,
MatNativeDateModule,
MatProgressBarModule,
MatRadioModule,
MatRippleModule,
]
})
export class AngularMaterialModule {}
tables-list.component.ts:
import { Component, OnInit, OnDestroy, ViewChild } from "@angular/core";
import { ProcessTable, ApplicationsTable } from "./tables.model";
import { PageEvent, MatTableDataSource, MatSort } from "@angular/material";
import { Subscription } from "rxjs";
import { TablesService } from "./tables.service";
import { AuthService } from "../auth/auth.service";
@Component({
// We load the component via routing and therefore we do not need a selector
selector: "app-tables",
templateUrl: "./tables-list.component.html",
styleUrls: ["./tables-list.component.css"]
}) // Turn class into component by adding @Component Decorator
export class TableListComponent implements OnInit, OnDestroy {
processTables: ProcessTable[] = [];
applicationsTables: ApplicationsTable[] = [];
isLoading = false;
totalTables = 0;
tablesPerPage = 5;
currentPage = 1;
pageSizeOptions = [1, 2, 5, 10];
dataSource = new MatTableDataSource(this.processTables);
@ViewChild(MatSort) sort: MatSort;
displayedprocessTablesColumns: string[] = [
"ProcessID",
"ProcessName",
"PackageVersion",
"RobotType",
"PackagePath",
"CreationTime",
"Status"
];
displayedApplicationsTablesColumns: string[] = [
"ProcessName",
"PackageVersion",
"WorkflowsBelongingToProcess",
"ApplicationsBelongingToWorkflow"
];
userIsAuthenticated = false;
userId: string;
isAdmin: boolean;
private tablesSub: Subscription;
private authStatusSub: Subscription;
constructor(
public tablesService: TablesService,
private authService: AuthService
) {}
ngOnInit() {
this.isLoading = true;
this.dataSource.sort = this.sort;
this.tablesService.getProcessTables(this.tablesPerPage, this.currentPage);
this.userId = this.authService.getUserId();
this.tablesSub = this.tablesService
.getTableUpdateListener()
.subscribe((tableData: { processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; tableCount: number }) => {
this.isLoading = false;
this.totalTables = tableData.tableCount;
this.processTables = tableData.processTables;
this.applicationsTables = tableData.applicationsTables;
});
this.userIsAuthenticated = this.authService.getIsAuth();
// console.log("Is authenticated: " + this.userIsAuthenticated);
this.authStatusSub = this.authService
.getAuthStatusListener()
.subscribe(isAuthenticated => {
this.userIsAuthenticated = isAuthenticated;
});
}
onLogout() {
this.authService.logout();
}
ngOnDestroy() {
this.tablesSub.unsubscribe();
this.authStatusSub.unsubscribe();
}
}
Tables.service.ts:
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Subject } from "rxjs";
import { map } from "rxjs/operators";
import { Router } from "@angular/router";
import { environment } from "../../environments/environment";
import { ProcessTable, ApplicationsTable } from "./tables.model";
const BACKEND_URL = environment.apiUrl + "/tables/";
@Injectable({ providedIn: "root" })
export class TablesService {
private processTables: ProcessTable[] = [];
private applicationsTables: ApplicationsTable[] = [];
private tablesUpdated = new Subject<{ processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; tableCount: number }>();
constructor(private http: HttpClient, private router: Router) {}
getProcessTables(tablesPerPage: number, currentPage: number) {
const queryParams = `?pagesize=${tablesPerPage}&page=${currentPage}`;
this.http
.get<{ processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; maxTables: number }>(
BACKEND_URL + queryParams
)
.pipe(
map((tableData: { processTables: ProcessTable[]; applicationsTables: ApplicationsTable[]; maxTables: number }) => {
return {
processTables: tableData.processTables.processTables.map(table => {
return {
ProcessID: table.ProcessID
ProcessName: table.ProcessName,
PackageVersion: table.PackageVersion,
RobotType: table.RobotType,
PackagePath: table.PackagePath,
CreationTime: table.CreationTime,
Status: table.Status
};
}),
applicationsTables: tableData.applicationsTables.applicationsTables.map(table => {
return {
ProcessName: table.ProcessName,
PackageVersion: table.PackageVersion,
WorkflowsBelongingToProcess: table.WorkflowsBelongingToProcess,
ApplicationsBelongingToWorkflow: table.ApplicationsBelongingToWorkflow
};
}),
maxTables: tableData.maxTables
};
})
)
.subscribe(transformedTablesData => {
this.processTables = transformedTablesData.processTables;
this.applicationsTables = transformedTablesData.applicationsTables;
this.tablesUpdated.next({
processTables: [...this.processTables],
applicationsTables: [...this.applicationsTables],
tableCount: transformedTablesData.maxTables
});
});
}
getTableUpdateListener() {
return this.tablesUpdated.asObservable();
}
}
Tables\model.ts:
export interface Table {
ProcessID: string;
ProcessName: string;
PackageVersion: string;
RobotType: string;
PackagePath: string;
CreationTime: string;
Status: string;
}
export interface ApplicationsTable {
ProcessName: string;
PackageVersion: string;
WorkflowsBelongingToProcess: string;
ApplicationsBelongingToWorkflow: string;
}
Backend\controllers\tables.js:
const sequelize = require("../sequelize");
const getProcessTables = (req, res) => {
return sequelize
.query("SELECT * FROM dbo.Process", { type: sequelize.QueryTypes.SELECT })
.then(fetchedtables => {
return {
message: "Process table fetched from the server",
processTables: fetchedtables,
maxProcessTables: fetchedtables.length
};
});
};
const getApplicationsTables = (req, res) => {
return sequelize
.query("SELECT * FROM dbo.Applications", {
type: sequelize.QueryTypes.SELECT
})
.then(fetchedtables => {
return {
message: "Applications Table fetched from the server",
applicationsTables: fetchedtables,
maxApplicationsTables: fetchedtables.length
};
});
};
exports.getTables = (req, res) => {
return Promise.all([
getApplicationsTables(req, res),
getProcessTables(req, res)
]).then(tables => {
res.status(200).json({
applicationsTables: tables[0],
processTables: tables[1]
});
});
};
Backend\routes\tables.js:
const express = require("express");
const TableController = require("../controllers/tables")
const router = express.Router({ mergeParams: true });
router.get("", TableController.getTables);
module.exports = router;
How can I fix it? Many Thanks Gennaro