Add new line in column name of dynamic material table in Angular - html

I have a material table with dynamic columns which currently look like -
But my expected output is like this -
What can i do to add new lines after PIN GROUNDED = 1 ? I tried adding BR but that didn't work .
My code for generating dynamic columns is -
<mat-table matSort [dataSource]="gridDataSource">
<ng-container [matColumnDef]="col" *ngFor="let col of columnsToDisplay">
<mat-header-cell mat-sort-header *matHeaderCellDef>
{{col | uppercase}}
</mat-header-cell>
<mat-cell *matCellDef="let element">
{{element[col]}}
</mat-cell>
</ng-container>
<mat-header-row class="headerHeight" *matHeaderRowDef="columnsToDisplay"></mat-header-row>
<mat-row *matRowDef="let row; columns: columnsToDisplay;"
[ngClass]="row.SEQ % 4 == 0 ? 'rows' : 'row'"></mat-row>
</mat-table>

With some research i got the solution .
We can use [innerHtml] property instead of {{colName}} . This makes the tag in the string reflect as a horizontal break line .
<span [innerHTML]= "col | uppercase"></span>

Related

Setting Angular Material mat-table column width in column configuration

I have the following column configuration for my Angular Material mat-table columns:
export interface TableColumn {
name: string;
dataKey: string;
isSortable?: boolean;
width?: string; // column width
}
//...
this.tableColumns = [
{
name: 'Id',
dataKey: 'id',
position: 'left',
width: '50px'
},
{
name: 'Name',
dataKey: 'name',
position: 'left',
width: '100%'
}
];
As far as I see, many people use css in order to set column width as on this page, etc. However, would not it be nice if we set the column widths for each column by pixel and percentage in the this.tableColumns array as the other properties when defining columns? I think I just need a configuration of mat-table, but I am not sure if there is such a config for mat-table. If not, I think I can use [style.width] and set each column by column config value. Any suggestion for that?
In general, you defined the columns based in your array like:
<ng-container *ngFor="let column of tableColumns;let first=first">
<ng-container [matColumnDef]="column.dataKey">
<th [style.width]="column.width" mat-header-cell *matHeaderCellDef>
{{column.name}}
</th>
<td [style.width]="column.width" mat-cell
*matCellDef="let element"> {{element[column.dataKey]}}
</td>
</ng-container>
</ng-container>
And your displayedColumns as
this.displayedColumns=this.tableColumns.map(x=>x.dataKey)
NOTE: I supose you defined the width as '50%' or '30px' if only use a number you can use, e.g.
[style.width]="column.width+'px'"
Update imagine you want to create a column with actions buttons, but this buttons can be one, two or three. We can asign to this last colum a width of "1%" and 'white-space: nowrap'
//we has two variables
btDelete:boolean=true;
btEdit:boolean:true;
<ng-container matColumnDef="action">
<th style="width:1%" mat-header-cell *matHeaderCellDef>
</th>
<td style="width:1%;white-space: nowrap;" mat-cell
*matCellDef="let element">
<button *ngIf="btEdit" mat-button (click)="edit(element)">Edit</button>
<button *ngIf="btDelete" mat-button (click)="delete(element)">Delete</button>
</td>
</ng-container>
NOTE: I'm not prety sure what happens with the width of columns because the % total is bigger that 100%
We can try using the mat-table in flex-mode
<ng-container *ngFor="let column of columns">
<ng-container [matColumnDef]="column.name">
<mat-header-cell [style.flex]="column.width" *matHeaderCellDef> {{column.width}}</mat-header-cell>
<mat-cell [style.flex]="column.width" *matCellDef="let element"> {{element[column.name]}} </mat-cell>
</ng-container>
</ng-container>
<ng-container matColumnDef="action">
<mat-header-cell style="flex:0 1 auto;visibility:hidden" *matHeaderCellDef>
<button *ngIf="btEdit" mat-button>Delete</button>
<button *ngIf="btDelete" mat-button>Delete</button>
</mat-header-cell>
<mat-cell style="flex:0 1 auto" *matCellDef="let element"> <button *ngIf="btEdit" mat-button>Edit</button>
<button *ngIf="btDelete" mat-button>Delete</button></mat-cell>
</ng-container>
See that the column "action" the "head" repeat the buttons -with visibility:hidden-
NOTE: In this case, the "with" -really the flex- is a number, not a %,
While there isn't a way to define column width in the array, you should be able to define it using ng-style. It is possible to provide column specific properties in the html template, therefore this too should work. An example would be
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<mat-text-column name="position" [headerText]="headerText" [ngStyle]="{'width':'20%'}"></mat-text-column>
<!-- Change the header text. -->
<mat-text-column name="name" headerText="Element" [ngStyle]="{'width':'40%'}"></mat-text-column>
<!-- Provide a data accessor for getting the cell text values. -->
<mat-text-column name="weight" [dataAccessor]="getWeight" [ngStyle]="{'width':'40%'}"></mat-text-column>
</table>
The result of style.width can also be accomplished by ngStyle, so that too should work in the same way.

Filling an angular table with data from http request

I am creating a table in angular and wish to fill it with data coming from an url . I have created a table following a tutorial. Code
dataSource : Parameters[]
getData(floor) {
console.log('Making a request')
this.vavService.getVavData(floor)
.subscribe(
(data11: any) => {
console.log(data11);
this.dataSource = data11;
console.log(this.dataSource)
}
)
}
parameters class
export class Parameters {
'date' : string;
'deviceID' : string;
'nvo_air_damper_position' : string;
'nvo_airflow' : string;
'nvo_temperature_sensor_pps' : string;
'timestamp' : string;
'vavID' : number;
'miloID' : string;
'miloTemperature' : number;
}
html code
<div>
<mat-table [dataSource]="dataSource">
<ng-container matColumnDef="date">
<mat-header-cell *matHeaderCellDef>Date</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<ng-container matColumnDef="deviceID">
<mat-header-cell *matHeaderCellDef>DeviceID</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<ng-container matColumnDef="damper">
<mat-header-cell *matHeaderCellDef>Damper Position</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<ng-container matColumnDef="airflow">
<mat-header-cell *matHeaderCellDef>Air Flow</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<ng-container matColumnDef="temperature">
<mat-header-cell *matHeaderCellDef>Temperature</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<ng-container matColumnDef="time">
<mat-header-cell *matHeaderCellDef>TimeStamp</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<ng-container matColumnDef="vavID">
<mat-header-cell *matHeaderCellDef>VAV ID</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<!-- <ng-container matColumnDef="miloID">
<mat-header-cell *matHeaderCellDef>Milo ID</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container>
<ng-container matColumnDef="miloTemperature">
<mat-header-cell *matHeaderCellDef>Milo Temperature</mat-header-cell>
<mat-cell *matCellDef="let policy"></mat-cell>
</ng-container> -->
<mat-header-row *matHeaderRowDef="tableColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: tableColumns"></mat-row>
</mat-table>
</div>
when i load the component, api call is successful and i can see the data in the console. Also I see the table headers in the browser
0: {date: "2019-12-03", deviceId: "fd00::212:4b00:1957:d616", nvo_air_damper_position: "100.0", nvo_airflow: "0.0", nvo_temperature_sensor_pps: "327.6700134277344", …}
1: {date: "2019-12-03", deviceId: "fd00::212:4b00:1957:d616", nvo_air_damper_position: "100.0", nvo_airflow: "0.0", nvo_temperature_sensor_pps: "327.6700134277344", …}
But the data binding is not happening. Can someone help me with binding the data. Thanks
Try this, hope it helps:
Set the correct dataSource type:
dataSource: MatTableDatsSource<Parameters> = new MatTableDataSource<Parameters>([]);
and set the data like:
this.dataSource.data = data11;
I think you have to reference your table and call the renderRows() function when you use the data that way instead of using MatTableDataSource because initialy your value is empty and filled when your observable emit a value.
If a data array is provided, the table must be notified when the array's objects are added, removed, or moved. This can be done by calling the renderRows() function which will render the diff since the last table render. If the data array reference is changed, the table will automatically trigger an update to the rows.
At mat-table tag, add #table1 <mat-table #table1>
In your component
Add #ViewChild('table1', {static: false}) table: MatTable<any>;
Then after your set the data, call this.table.renderRows();

Is there any way to edit specific Column of table using mat-table in Angular material

In my project i am using Angular Material's table for displaying value in table format, As per new requirement i have to perform in-line editing for last 2 column over other along with that if user click 1st column the other column get highlighted automatically and everything has to be done using Angular material
This is the last 2 column i want to perform in-line editing
> <ng-container matColumnDef="weight">
> <th mat-header-cell *matHeaderCellDef> Weight </th>
> <td mat-cell *matCellDef="let element"> {{element.weight}} </td>
> </ng-container>
> <ng-container matColumnDef="symbol">
> <th mat-header-cell *matHeaderCellDef> Symbol </th>
> <td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
> </ng-container>
>
>
> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> </table>
How i achieved is as follows :
<tbody>
<tr *ngFor="let data of dataSource">
<td >{{data.position}}</td>
<td >{{data.name}}</td>
<td *ngIf="data.position === editRowId"> <input matInput [(ngModel)]="data.weight"></td>
<td *ngIf="data.position !== editRowId" (click)="editTableRow(data.position)">{{data.weight}}</td>
<td *ngIf="data.position === editRowId"> <input matInput [(ngModel)]="data.symbol"></td>
<td *ngIf="data.position !== editRowId" (click)="editTableRow(data.position)">{{data.symbol}}</td>
</tr>
</tbody>
TS file for above code :
export class AppComponent {
showEditTable = false;
editRowId: any = '';
selectedRow;
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
dataSource = ELEMENT_DATA;
editTableRow(val) {
console.log('click event started val = ' + val);
this.editRowId = val;
console.log('click event ended with val = ' + val);
}
}
I expect the result as table where last 2 column can be edited inline and at the same time i can send modified data to backend
Naman, it's the same, you need use <ng-container>to avoid create extra tags, so your columns becomes like
<!-- Weight Column -->
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef> Weight </th>
<td mat-cell *matCellDef="let element">
<ng-container *ngIf="element.position!==editRowId">
<span (click)="edit(element.position,'weigth')">{{element.weight}} </span>
</ng-container>
<ng-container *ngIf="element.position===editRowId">
<input matInput name="weigth" [(ngModel)]="element.weight">
</ng-container>
</td>
</ng-container>
Well, I call to function "edit" passign two arguments, the position and a string indicate the "name" of the input attributes. This allow us "focus" the input clicked. how?
We declare a ViewChildren of the MatInputs
#ViewChildren(MatInput,{read:ElementRef}) inputs:QueryList<ElementRef>;
See that we get not the MatInput else the "ElementRef". This allow us, in out function edit get the element with the attributes name equal the string that pass as argument, and focus it. See that we need "enclosed" all in a setTimeout to allow Angular to show the input
edit(row,element)
{
this.editRowId=row;
setTimeout(()=>{
this.inputs.find(x=>x.nativeElement.getAttribute('name')==element)
.nativeElement.focus()
})
}
You can see the full example in stackblitz
Well, in the example the data is hardcoded. Let's go to imagine that the data (and the structure) comes from a service data. The data is easy imagine because it's the same. The "structure" we can imagine as an array of object with three properties: name,head ad fixed. if fixed is true, we only show the data, else we can edit. So the only thing we need is create the columns in a *ngFor
First we are going to see how our schema can be defined. It's only an array
[
{name:'position',head:"No.",fixed:true},
{name:'name',head:"Name",fixed:true},
{name:'weight',head:"Weigth",fixed:false},
{name:'symbol',head:"Symbol",fixed:false},
]
Our table becomes like
<table #table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container *ngFor="let column of schema;let last=last">
<ng-container [matColumnDef]="column.name">
<th mat-header-cell *matHeaderCellDef> {{column.head}} </th>
<td mat-cell *matCellDef="let element">
<ng-container *ngIf="element[schema[0].name]!==editRowId || column.fixed">
<span
(click)="column.fixed?editRowId=-1:
edit(element[schema[0].name],column.name)">
{{element[column.name]}}
</span>
</ng-container>
<ng-container *ngIf="element[schema[0].name]===editRowId && !column.fixed">
<input matInput [id]="column.name"
[(ngModel)]="element[column.name]"
(blur)="last?editRowId=-1:null">
</ng-container>
</td>
</ng-container>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
See that we "replace" element.position by element[column[0].name] -I supouse the first element of the schema will be the "key" and how we using [(ngModel)]="elemen[column.name]". Yes, to refererd to element.position we can refered too as elemen["position"] and remember we are iterating over "schema"
Another thing is that we are going to use "id", not "name". this is because if we using name, really Angular put as attrib some like: ng-reflect-name, and this don't allow us focus the input.
Finally we are get the data in the ngOnInit. We are going to use forkJoin to get together the schema and the data. A forkJoin only call and array of observables (in this case this.dataService.getSchema() and this.dataServide.getData, and return in an array the response of all the observables. We use the way
([variable1,variable2]) to store in "variable1" the first result and in variable2 the second result
ngOnInit()
{
forkJoin([this.dataService.getSchema(),this.dataService.getData()])
.subscribe(([schema,data])=>{
this.dataSource=data;
this.displayedColumns=schema.map(x=>x.name)
this.schema=schema
})
}
The displayedColumns must be an array with the names of the columns, but we need to store in an array the "schema" too.
In the stackblitz I create a service and "simulate" the observable using the rxjs creation operator of, in a real application the data becomes from a httpClient.get(....)

How to show a empty message in dynamic data table angular

I try to show an empty message error when the filter doesn't have matches with:
<div *ngIf="dataSource.length === 0">No data</div>
but it doesn't work because I build a dynamic table with MatTableDataSource.
For a better understanding, I changed my dynamic table for an array predefined.
const ELEMENT_DATA: MembersElement[] = [
{ name: 'Jenny', age: 17 },
{ name: 'Daniel', age: 18 }
];
However, is necessary using MatTableDataSource to a dynamic building of the users' table
This is all my ts code.
import { Component, OnInit } from '#angular/core';
import {MatTableDataSource} from '#angular/material';
export interface SociosElement {
nombre: string;
edad: number;
}
const ELEMENT_DATA: MembersElement[] = [
{ name: 'Jenny', age: 17 },
{ name: 'Daniel', age: 18 }
];
#Component({
selector: 'app-pruebas',
templateUrl: './tableMembers.component.html',
styleUrls: ['./tableMembes.component.css']
})
export class PruebasComponent {
displayedColumns: string[] = ['name', 'age'];
dataSource = new MatTableDataSource(ELEMENT_DATA);
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
}
This is my HTML code.
<mat-toolbar color="primary">My full table</mat-toolbar>
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
<mat-table #table [dataSource]="dataSource">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header>Name </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
</ng-container>
<ng-container matColumnDef="age">
<mat-header-cell *matHeaderCellDef mat-sort-header>Age </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.age}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;" (click)="row.toggle(row)">
</mat-row>
</mat-table>
<div *ngIf="dataSource.length === 0">No data</div>
You can't reach what you want using *ngIf="dataSource.length === 0" condition simply because the data source doesn't change at all when you do filtering. what you have to do is watch out for the filtered data length. you can use something like following:
*ngIf="dataSource.filteredData.length > 0" as the condition. the length of datasource.filteredData changes based on the filtered results. this condition can hide your table. you can put this in your table tag like:
<table mat-table [dataSource]="dataSource" *ngIf="dataSource.filteredData.length > 0">
One caveat to using ngIf. If you're using matSort and you nest the table inside a block using *ngIf, then the sorting will not work because the ngIf sets the viewChild to undefined. Reference
Use [ngClass] to get around this
<div [ngClass]="dataSource.filteredData.length > 0 ? 'visible': 'hidden'">
<mat-table #table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header>Name </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
</ng-container>
<ng-container matColumnDef="age">
<mat-header-cell *matHeaderCellDef mat-sort-header>Age </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.age}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;" (click)="row.toggle(row)">
</mat-row>
</mat-table>
</div>
<div [ngClass]="dataSource.filteredData.length > 0 ? 'hidden': 'visible'">
<tr>No results found.</tr>
</div>
Here is the CSS for the classes
.hidden {
visibility: hidden;
}
.visible {
visibility: visible;
}
For a mat-table, prior to v10, you could give the table a row of data when your dataSource is empty by doing this:
[dataSource]="dataSource.data.length > 0 && dataSource.data.filteredData > 0 ? dataSource : emptyDataSource"
Then create a column def for your cell to display the empty data message:
<ng-container vdlColumnDef="empty-row"> <td *matCellDef="let row" mat-cell [colSpan]="displayedColumns.length">No Data</td> </ng-container>
Then change your row def to show the empty row column def when dataSource is empty:
<tr mat-row *matRowDef="let row; columns: dataSource.data.length > 0 && dataSource.data.filteredData > 0 ? displayedColumns : ['empty-row'];">
https://stackblitz.com/edit/angular-mat-table-no-data?file=src%2Fapp%2Ftable-basic-flex-example.html
After Angular Material 10, they've added a directive for when there is no data in the table: 'If you want to show a message when not data matches the filter, you can use the *matNoDataRow directive.' https://v10.material.angular.io/components/table/overview#filtering

merge cells for some records in Angular table

I am new to angular the following is the scenario:
I have a table with 10 columns.
Suppose I have different status like children,teen,young,adult,senior in column 4.
While displaying all records in Angular table, the records with senior status I need to display only 1,2,3& 4 columns with data and from 5-10 columns would be blank(actually there is data for these columns that is getting populated form data source but I need to show that as blank) and merged.
<mat-table #table [dataSource]="ds" matSort matSortDisableClear>
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition. -->
<!-- Request ID Column -->
<ng-container matColumnDef="id">
<mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">
Req Id
</mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.id}} </mat-cell>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="firstName">
<mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">
Name
</mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.firstName}}<br />{{row.lastName}} </mat-cell>
</ng-container>
<!-- address Column -->
<ng-container matColumnDef="address">
<mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">
address
</mat-header-cell>
<mat-cell *matCellDef="let row">
{{row.address}}
</mat-cell>
</ng-container>
<!-- Status Column -->
<ng-container matColumnDef="status">
<mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">
Status
<mat-cell *matCellDef="let row">
{{row.status}} </mat-cell>
</ng-container>
....
<!-- Other 6 columns that needs to be merged similar to Id name and address -->
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
Thanks in advance
By using a ng-container like this for each column where we need to verify Removed status and display data based on that