Angular Material Table not displaying data from API service - angular-material

I've checked the other questions on this topic here but none seemed to solve my problem. I'm pulling a list of noticeboard notices from an API as an observable and trying to display them in a table. The table headers render but no rows. I can get the data to render if I use Pre tags and the variable but assigning it to dataSource isn't working - and no errors.
Service snippet
getObjectWithParams(object: any, params: any): Observable<any> {
this.ResourcePath = object;
const NewQueryOptions = this.QueryOptions + params;
return this.http.get(`${this.ServiceRoot}${this.ResourcePath}${NewQueryOptions}`, { 'headers': this.headers });
}
notices.component.ts
export class NoticesComponent implements OnInit {
public notices!: INotice[];
displayedColumns: string[] = ['ReleaseDate', 'ReleaseTag','Status', 'Title']
dataSource!: MatTableDataSource<INotice>;
pageSize = 10;
pageIndex = 0;
pageLength = 0;
private _top: number | undefined;
private _skip: number | undefined;
constructor( private OData: ODataService, private _location: Location) {}
ngOnInit(): void {
this.getNotices('/notices', '$count=true&$top=10');
}
public getNotices(table: string, params: string) {
this.OData.getObjectWithParams(table, params)
.subscribe( data => {
this.notices = data,
this.dataSource = new MatTableDataSource<INotice>(this.notices);
});
}
}
notices.component.html
<Div *ngIf="dataSource">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
<ng-container matColumnDef="ReleaseDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Release Date </th>
<td mat-cell *matCellDef="let notice"> {{notice.ReleaseDate}} </td>
</ng-container>
<ng-container matColumnDef="ReleaseTag">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Release Tag </th>
<td mat-cell *matCellDef="let notice"> {{notice.ReleaseTag}} </td>
</ng-container>
<ng-container matColumnDef="Status">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
<td mat-cell *matCellDef="let notice"> {{notice.Status}} </td>
</ng-container>
<ng-container matColumnDef="Title">
<th mat-header-cell *matHeaderCellDef> Title </th>
<td mat-cell *matCellDef="let notice"> {{ notice.Title}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<!--<pre> {{ notices | json }}</pre> -->
</Div>
I'm hoping another set of eyes may spot the issue easily so appreciate any insights in what I'm doing wrong.
Thanks.

It seems there is a problem with response parsing.
public getNotices(table: string, params: string) {
this.OData.getObjectWithParams(table, params)
.subscribe( response => {
this.notices = response.data, //this is actual response object
this.dataSource = new MatTableDataSource<INotice>(this.notices);
});
}
}

Thanks To #Rushit and #Kiran for the input. It was enough to make me dig deeper in the code and I found the issue.
I hadn't realised that the OData service wasn't removing some OData decorators on the return data which was messing up further manipulation.
As an FYI to anyone else this was how I solved it:
Corrected the OData Service to remove metadata using a QueryOptions variable
public QueryOptions = '?$format=application/json;odata.metadata=none'
...
getObjectWithParams(object: any, params: any): Observable<any> {
this.ResourcePath = object;
const NewQueryOptions = this.QueryOptions + params;
return this.http.get(`${this.ServiceRoot}${this.ResourcePath}${this.QueryOptions}`, { 'headers': this.headers });
}
And then as Rushit pointed out the input needed adjusting from response to response.value:
public getNotices(table: string, params: string) {
this.OData.getObjectWithParams(table, params)
.subscribe( response => {
this.notices = response.value,
this.dataSource = new MatTableDataSource<INotice>(this.notices);
});
}
}

Related

I can't display a column from the dataSource in an angular material table

I have this dataSource
In my filtro component, I call to listado-proyectos component and I pass it dataSource
Filtro
<app-listado-proyectos [proyectos]="dataSource" [origen]="'certificados'"
*ngIf="dataSource.length>0"></app-listado-proyectos>
Listado Proyectos
#Component({
selector: 'app-listado-proyectos',
templateUrl: './listado-proyectos.component.html',
styleUrls: ['./listado-proyectos.component.scss']
})
export class ListadoProyectosComponent implements OnInit, OnChanges {
#Input()
proyectos: IProyecto[];
#Input()
origen: string;
dataSource: MatTableDataSource<IProyecto>
columnsToDisplay: string[] = [];
filtro: string;
#ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
constructor(private generalService: GeneralService) { }
ngOnChanges(changes: SimpleChanges): void {
this.columnsToDisplay = ['codigo', 'descripcionCorta', 'jefeGrupo', 'responsable',
'estado', 'cliente', 'fechaInicio', 'fechaFin', 'presupuesto', 'facturable',
'certificado'];
this.dataSource = new MatTableDataSource(this.proyectos);
console.log("Listado de proyectos en ListadoProyectosComponent", this.dataSource);
}
<mat-table [dataSource]="dataSource" matTableExporter #exporter="matTableExporter"
multiTemplateDataRows matSort class="mat-elevation-z8">
......
<ng-container matColumnDef="certificado" *ngIf="origen=='certificado'">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Certificado</th>
<td mat-cell *matCellDef="let element">{{element.certificado}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay; sticky:true"></tr>
<tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>
</mat-table>
in listado component I get the data
But I get this error
Any idea, please?
Thanks
<mat-table [dataSource]="dataSource" matTableExporter #exporter="matTableExporter" multiTemplateDataRows matSort
class="mat-elevation-z8">
.....
<ng-container matColumnDef="certificado" *ngIf="origen=='certificado'">
<th mat-header-cell *matHeaderCellDef>Certificado</th>
<td mat-cell *matCellDef="let element">{{element.certificado}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay; sticky:true"></tr>
<tr mat-row *matRowDef="let row; columns: columnsToDisplay;"></tr>
</mat-table>

Angular Material v13 Table not Sorting

I made an Angular Material Table and now I'm trying to implement Sort. I build the table following the documentation for version 13, but the sorting doesn't work (even though the header is clickable and the sorting arrow changes direction on click.
Here is my component:
import { Component, VERSION, ViewChild } from '#angular/core';
import { MatSort } from '#angular/material/sort';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
#ViewChild(MatSort, { static: true }) sort!: MatSort;
dataSource = [
{ value: 'One %value% none', type: 'Type is %type%', time: 1 },
{ value: 'Another %value%', type: 'This type is %type%', time: 3 },
{ value: '%value% none', type: 'The type is %type%', time: 2 },
];
displayedColumns: string[] = Object.keys(this.dataSource[0]);
name = 'Angular ' + VERSION.major;
}
And here is the html:
<p>Table works</p>
<table mat-table matSort [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Value Column -->
<ng-container matColumnDef="value">
<th mat-sort-header mat-header-cell *matHeaderCellDef>Vaule</th>
<td mat-cell *matCellDef="let element">{{ element.value }}</td>
</ng-container>
<!-- Type Column -->
<ng-container matColumnDef="type">
<th mat-sort-header mat-header-cell *matHeaderCellDef>Type</th>
<td mat-cell *matCellDef="let element">{{ element.type }}</td>
</ng-container>
<!-- Time Column -->
<ng-container matColumnDef="time">
<th mat-sort-header mat-header-cell *matHeaderCellDef>Time</th>
<td mat-cell *matCellDef="let element">{{ element.time }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
The full reproducible example can be found here:
stackblitz
ps: there are a few similar questions in SO, but I couldn't find one with this version and this specifications.

MatSort and MatPaginator not being initialized using #ViewChild and ngAfterViewInit if inside *ngIf

I am using a mat table with paginator and sort within an *ngIf. The table data is retrieved using an API.
This is the component's typescript code:
export class UserPermissionAdminComponent implements AfterViewInit, OnInit {
dataSource: MatTableDataSource<UserPermission> = new MatTableDataSource<UserPermission>();
columnsToDisplay: string[] = ['id', 'name'];
#ViewChild(MatSort, { static: false }) sort!: MatSort;
#ViewChild(MatPaginator, { static: false }) paginator!: MatPaginator;
constructor(private userPermService: UserPermissionService) {
}
ngOnInit(): void {
this.userPermService.getAllUserPermissions()
.subscribe(recvPerms => this.dataSource.data = recvPerms);
}
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
console.log(this.dataSource.sort);
}
applyFilter(event: Event): void {
const filterValue = (event.target as HTMLInputElement).value;
this.dataSource.filter = filterValue.trim().toLowerCase();
}
}
And this is the component's template:
<div class="container">
<h2>Permisiuni utilizatori</h2>
<ng-container *ngIf="dataSource.data.length; then existingPermissions; else noPermissions">
</ng-container>
<ng-template #existingPermissions>
<div>
<div class="filter-container">
<mat-form-field>
<mat-label>Cautare</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Ex: READ" #input />
</mat-form-field>
</div>
<div class="table-container mat-elevation-z8">
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ID </th>
<td mat-cell *matCellDef="let permission"> {{permission.id}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Denumire permisiune </th>
<td mat-cell *matCellDef="let permission"> {{permission.name}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
<tr mat-row *matRowDef="let rowData; columns: columnsToDisplay"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5, 10, 20]"></mat-paginator>
</div>
</div>
</ng-template>
<ng-template #noPermissions>
<div>
<span class="notification">Nu a fost gasita nicio permisiune utilizator.</span>
</div>
</ng-template>
</div>
The issue is that sorting and pagination do not work (the console.log also lists that this.sort is undefined). The table contains all the data on a single page. Filtering also works.
I have managed to fix the issue by adding setters to the paginator and sort:
#ViewChild(MatSort, { static: false }) set sort(val: MatSort) {
if (val) {
this.dataSource.sort = val;
}
}
#ViewChild(MatPaginator, { static: false }) set paginator(val: MatPaginator) {
if (val) {
this.dataSource.paginator = val;
}
}
However, the code now works even without ngAfterViewInit. Can someone please explain how and why this works?

Angular Material MatPaginator showing page 0 of 0

sorry for the question in advance. I googled my problem before writing this and tried lots of solutions, but without luck.
I'm an Angular noob trying to learn Material.
My simple project makes a http request and uses the JSON received to populate a table.
The Angular Material table is working fine, but the paginator is not.
It is greyed and shows page 0 of 0.
The component.html is as follows:
<div style="height: 80vh;">
<mat-toolbar color="primary">
<mat-toolbar-row>
<button mat-icon-button (click)="sidenav.toggle()" fxShow="true" fxHide.gt-sm>
<mat-icon>menu</mat-icon>
</button>
<span>Teste - Requisição Http/Tabela</span>
<span class="menu-spacer"></span>
</mat-toolbar-row>
</mat-toolbar>
<mat-sidenav-container fxFlexFill>
<mat-sidenav #sidenav>
<mat-nav-list>
<a (click)="sidenav.toggle()" mat-list-item>Fechar</a>
<a href="#" mat-list-item>Link 1</a>
<a href="#" mat-list-item>Link 2</a>
<a href="#" mat-list-item>Link 3</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content fxFlexFill>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef> Id </th>
<td mat-cell *matCellDef="let element"> {{element.id}} </td>
</ng-container>
<ng-container matColumnDef="email">
<th mat-header-cell *matHeaderCellDef> Email </th>
<td mat-cell *matCellDef="let element"> {{element.email}} </td>
</ng-container>
<ng-container matColumnDef="first_name">
<th mat-header-cell *matHeaderCellDef> First Name </th>
<td mat-cell *matCellDef="let element"> {{element.first_name}} </td>
</ng-container>
<ng-container matColumnDef="last_name">
<th mat-header-cell *matHeaderCellDef> Last Name </th>
<td mat-cell *matCellDef="let element"> {{element.last_name}} </td>
</ng-container>
<ng-container matColumnDef="avatar">
<th mat-header-cell *matHeaderCellDef> Avatar </th>
<td mat-cell *matCellDef="let element"> {{element.avatar}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
The component.ts is as follows:
import { HttpClient } from '#angular/common/http';
import { AfterViewInit, Component, ViewChild } from '#angular/core';
import { MatPaginator } from '#angular/material/paginator';
import { MatTableDataSource } from '#angular/material/table';
export interface UserData {
id: number;
email: string;
first_name: string;
last_name: string;
avatar: string;
}
const USER_DATA: UserData[] = [];
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
title = 'Test';
url = 'https://reqres.in/api/users?page=1';
displayedColumns: string[] = ['id', 'email', 'first_name', 'last_name', 'avatar'];
dataSource = new MatTableDataSource<UserData>(USER_DATA);
constructor(private http: HttpClient) {
this.http.get(this.url).toPromise().then(data => {
console.log(data["data"]);
this.dataSource = data["data"]
console.log(this.dataSource)
});
}
#ViewChild(MatPaginator) paginator: MatPaginator;
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
}
}
What am I doing wrong?
Thanks
Try adding #paginator in
<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>
It should be:
<mat-paginator #paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>
Without the id "#paginator" the #ViewChild decorator will never be able to find the element (this id must be the same as the used in the #ViewChild decorator line).
This little detail is not included in the Pagination example in the Angular Material documentation (or I was not able to see it..)
Beside this, try declaring the dataSource object as:
dataSource! = MatTableDataSource<UserData>;
instead of
dataSource = new MatTableDataSource<UserData>(USER_DATA);
Then, when the promise is resolved, assign the contents this way
this.dataSource = new MatTableDataSource<UserData>(data.data);
instead of
this.dataSource = data["data"]
Immediately after that, assign the paginator component to the dataSource.paginator:
this.dataSource.paginator = this.paginator;
Try removing the AfterView and replace the ViewChild with the below code:
#ViewChild(MatPaginator) set matPaginator(paginator: MatPaginator) {
this.dataSource.paginator = paginator;
}
In the angular's documentation, they forgot to add, that you need to define { static: false} or {static: true} .
Like this:
#ViewChild(MatPaginator, {static:false}) paginator: MatPaginator;
in docs:
static - True to resolve query results before change detection runs,
false to resolve after change detection. Defaults to false.
You need to set it to false, if you want to use default.
I needed to add this.paginator.length = +data.meta.total; so that length prop of paginator knew the total possible items, and could calculate the pagination.
Adding, the example from ng docs doesn't show this and docs show as 'hard coded' values.
ng docs need work!

Data-table don't show my DataSource Array

I have a problem that I do not know how to solve it from my component I send an array of Resource so that another component reads it and injects it into a DataSource that is read by a mat-table. Consulted the documentation of Angular, I see that it is correct. I do not know if it's something in the html that does not allow me to see it. Can someone tell me the reason?
Thank you so much
my .ts
#Input() resources : Resource [];
public dataSource = new MatTableDataSource <Resource> ();
public ngOnInit(): void {
console.log(this.resources)
this.dataSource.data = this.resources
}
my .html
<mat-table class="resources-table" #table [dataSource]="resources" matSort [#animateStagger]="{value:'50'}" fusePerfectScrollbar>
<!-- ID Column -->
<ng-container matColumnDef="_id">
<mat-header-cell *matHeaderCellDef mat-sort-header>ID</mat-header-cell>
<mat-cell *matCellDef="let resource">
<p class="text-truncate">{{resource._id}}</p>
</mat-cell>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header>{{'RESOURCE.NAME' | translate}}</mat-header-cell>
<mat-cell *matCellDef="let resource">
<p class="text-truncate">{{resource.name}}</p>
</mat-cell>
</ng-container>
</mat-table>
console.log(this.resources) is img:
Try to set DataSource property of Mat-Table in which you assinging the data:
HTML code:
\/ <-- Here
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="_id">
<th mat-header-cell *matHeaderCellDef mat-sort-header> No. </th>
<td mat-cell *matCellDef="let element"> {{element._id}} </td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
TS code:
import { Component, OnInit, ViewChild } from '#angular/core';
import { MatSort, MatTableDataSource } from '#angular/material';
#Component({
selector: 'table-sorting-example',
styleUrls: ['table-sorting-example.css'],
templateUrl: 'table-sorting-example.html',
})
export class TableSortingExample implements OnInit {
displayedColumns: string[] = ['_id', 'name'];
dataSource = new MatTableDataSource([]);
#ViewChild(MatSort) sort: MatSort;
ngOnInit() {
var elements = [{ _id: 1, name: 'Paul Walker' },
{ _id: 2, name: 'Lisa' }];
this.dataSource = new MatTableDataSource(elements); // Set dataSource like this
this.dataSource.sort = this.sort; // and then assign sort
}
}
Stackblitz

Resources