Angular mat-table displaying repeat rows - javascript

I am trying to create a relatively simple mat-table that displays elements from a "dataSource". My problem is that when I run my code it gives me the error: There can only be one default row without a when predicate function.
Adding multiTemplateDataRows to my <mat-table> fixes this problem but makes it so that all of the elements in my table are duplicated (two of them).
Picture displaying duplication
I want to understand why is is happening. I believe that in my case the items are duplicated because I have exactly two columns. Can someone help me understand?
My Code:
HTML:
<mat-table [dataSource]="siteContacts" multiTemplateDataRows class="table" fxLayout="column wrap">
<ng-container [matColumnDef]="col" *ngFor="let col of siteContactColumns">
<mat-header-cell *matHeaderCellDef>
</mat-header-cell>
<mat-cell *matCellDef="let element">
<div [ngSwitch]="siteContactDataSchema[col]">
<div *ngSwitchCase="'ContactTitle'">
{{element[col]}}
</div>
<span *ngSwitchDefault>
{{element[siteContactDataSchema[col][0]]}} <br>
{{element[siteContactDataSchema[col][1]]}} <br>
{{element[siteContactDataSchema[col][2]]}}
</span>
</div>
</mat-cell>
<mat-row *matRowDef="let row; let even = even; columns: siteContactColumns;" [ngClass]="{gray: even}">
</mat-row>
</ng-container>
</mat-table>
Typescript:
const SITE_CONTACTS_SCHEMA = {
"ContactTitle": "ContactTitle",
"ContactInfo": ["ContactName", "ContactCellPhone", "ContactEmail"]
}
...
siteContactDataSchema = SITE_CONTACTS_SCHEMA;
siteContactColumns: string[] = ['ContactTitle', 'ContactInfo'];
...
this.siteDirectoryService.getAllSiteContacts(this.parentSiteId).subscribe(res => {
this.siteContacts = res;
})

Related

How to sort primeNg table with nested columns structure?

I am using primeNg . I want to implement sorting of data. The problem is that I have a column data structure where some columns have a superColumn type and that means they are divided for a set of subColumns. It works well for presenting but I have a problem with sorting it. It is never sorted on basis of the subcolumn field value but on the basis of the parent column (supercolumn) field value. That means it is taking what is in the [pSortableColumn] attribute instead of what is in the [field] attribute of the sorting icon.
Is there any way I could resolve that? This is my code below (I removed styling):
<th
*ngFor="let col of columns; let i = index;"
pResizableColumn
pReorderableColumn
[pSortableColumn]="col.field"
[pSortableColumnDisabled]="col.showSort === false"
>
<ng-container *ngIf="col.type !== 'superColumn'">
<span>{{ col.header }}</span>
<p-sortIcon [field]="col.field"></p-sortIcon>
</ng-container>
<ng-container *ngIf="col.type === 'superColumn'">
<div>{{ col.header }}</div>
<ng-container *ngFor="let subColumn of col.subColumns">
<div>
{{ subColumn.header }}
<p-sortIcon [field]="subColumn.field"></p-sortIcon>
</div>
</ng-container>
</ng-container>
</th>

show and hide between 2 different component Angular

I have Parent component with 2 different child, inquiryForm and inquiryResponse, I got situation when I need to hide and show this 2 component based on condition:
If user had click submit on inquiryForm, it will hide inquiryForm component and show inquiryResponse component
On inquiryResponse component, there are button display inquiry form, where user clicked it and will hide inquiryResponse component and show inquiryForm.I cant solved this.
I know it can be solved using router but I want different solution like using service or subject
this is demo I created using stackblitz, this is what I had tried;
inquiry-response.ts
getReceivedSummons() {
this.inquiryStore.summons$.subscribe(result => {
this.receivedSummon = result;
this.addCheckboxes();
this.isShowResponse = true;
});
}
showInquiryForm() {
// do something
}
inquiry-response.html
<div *ngIf="isShowResponse">
<p>Inquiry Response</p>
<form [formGroup]="form" (ngSubmit)="submitSelectedCheckboxes()">
<ng-container formArrayName="receivedSummons" *ngFor="let summon of formReceivedSummons.controls; let i = index">
<ng-container [formGroup]="summon">
<ng-container formArrayName="items" *ngFor="let item of formReceivedSummonsItems(i).controls; let j = index">
<ng-container [formGroup]="item">
<input type="checkbox" formControlName="isChecked"> {{item.value.name}}
</ng-container>
</ng-container>
</ng-container>
<div *ngIf="!summon.valid">At least one order must be selected</div>
</ng-container>
<br>
<span class="button">
<button [disabled]="!form.valid">submit</button>
</span>
<button (click)="showInquiryForm()"> ( change ID number ) display inquiry form</button>
</form>
</div>
As AJT_82 say, you app.component can be like
<app-inquiry-form *ngIf="step==1" (submit)="step=2"></app-inquiry-form>
<br>
<app-inquiry-response *ngIf="step==2" (click)="step=1"></app-inquiry-response>
//you has a variable
step:number=1;
And in each component
#Output() submit=new EventEmitter<any>()
..in somewhere...
this.submit.emit()
#Output() click=new EventEmitter<any>()
..in somewhere...
this.click.emit()

How to make a function in angular 7

I have a mat-table with 6 columns. In 5th column it shows the status of Job i.e Completed,Running or Pending.
I have created two buttons i.e Stop and Re-Run.
I want to write function that if job is completed then Stop button should be disabled and re-run should be enabled which means that the user should be able to re-run the completed job and if job is executing or pending(waiting to execute) then Stop button should be enabled and re-run should be disabled which means that the user should be able to stop the running Job.
Can someone help me ?
I have created a sample stackblitz here.
HTML Code:
<div class="main-content">
<mat-toolbar>
<mat-progress-bar
mode="indeterminate"
class="mat-progress-bar"
color ="primary"
>
</mat-progress-bar>
<button
mat-icon-button
(click)="stop_exec_job()"
matTooltip="Stop Executing the Job"
>
<!-- Edit icon for row -->
<i class="material-icons" style="color:red"> stop </i>
</button>
</mat-toolbar>
<div class="card">
<div class="card-header">
<h5 class="title">Job Execution Stats</h5>
</div>
<mat-table [dataSource]="jobExecutionStat">
<!-- Id Column -->
<ng-container matColumnDef="id">
<mat-header-cell *matHeaderCellDef> ID </mat-header-cell>
<mat-cell *matCellDef="let element">{{ element.id }} </mat-cell>
</ng-container>
<ng-container matColumnDef="exec_date">
<mat-header-cell *matHeaderCellDef>
Execution Date
</mat-header-cell>
<mat-cell *matCellDef="let element"
>{{ element.exec_date }}
</mat-cell>
</ng-container>
<!-- Weight Column -->
<ng-container matColumnDef="curr_time_period">
<mat-header-cell *matHeaderCellDef>
Current Time Period
</mat-header-cell>
<mat-cell *matCellDef="let element"
>{{ element.curr_time_period }}
</mat-cell>
</ng-container>
<!-- Symbol Column -->
<ng-container matColumnDef="prev_time_period">
<mat-header-cell *matHeaderCellDef>
Previous Time Period
</mat-header-cell>
<mat-cell *matCellDef="let element"
>{{ element.prev_time_period }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="status">
<mat-header-cell *matHeaderCellDef> Status </mat-header-cell>
<mat-cell *matCellDef="let element"
>{{ element.status }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef> </mat-header-cell>
<mat-cell *matCellDef="let element; let index = index">
<button
mat-icon-button
(click)="stop_exec_job()"
matTooltip="Stop Executing the Job"
>
<!-- Edit icon for row -->
<i class="material-icons" style="color:red"> stop </i>
</button>
<!-- Delete icon for row -->
<button
class="stop_btn"
mat-icon-button
color="#b71c1c"
(click)="re_run_job()"
matTooltip="Re-Run the Job"
>
<i class="material-icons" style="color:green">
cached
</i>
</button>
</mat-cell>
</ng-container>
<mat-header-row
*matHeaderRowDef="jobExecStatDisplayedColumns"
></mat-header-row>
<mat-row *matRowDef="let row; columns: jobExecStatDisplayedColumns">
</mat-row>
</mat-table>
</div>
Typescript Code:
import { Component, OnInit } from "#angular/core";
import { MatTableDataSource } from "#angular/material";
import { GlobalAppSateService } from '../../services/globalAppSate.service';
import { DataService } from '../../services/data.service';
import { SnakBarComponent } from '../custom-components/snak-bar/snak-
bar.component';
#Component({
selector: "app-job-execution-screen",
templateUrl: "./job-execution-screen.component.html",
styleUrls: ["./job-execution-screen.component.scss"]
})
export class JobExecutionScreenComponent implements OnInit {
stop_btn: boolean = true;
re_run_btn: boolean = true;
jobExecStatDisplayedColumns = [
"id",
"exec_date",
"prev_time_period",
"curr_time_period",
"status",
"actions"
];
jobExecutionStat = new MatTableDataSource<Element>(ELEMENT_DATA);
constructor(private dataService: DataService, public globalAppSateService:
GlobalAppSateService, private snakbar: SnakBarComponent ) {
}
ngOnInit() {
const project = JSON.parse(this.dataService.getObject("project"));
if (project != null) {
this.globalAppSateService.onMessage(project);
}
}
stop_exec_job() {
}
re_run_job() {
}
}
const ELEMENT_DATA: Element[] = [
{
id: 1,
exec_date: "17-01-2016",
prev_time_period: "2016-04,2016-05,2016-06",
curr_time_period: "2016-08",
status: "Completed"
},
{
id: 2,
exec_date: "17-01-2017",
prev_time_period: "2017-04,2017-05,2017-06",
curr_time_period: "2017-08",
status: "Running"
},
{
id: 3,
exec_date: "27-07-2017",
prev_time_period: "2017-45,2017-46,2017-47",
curr_time_period: "2018-01,2018-02",
status: "Pending"
},
{
id: 4,
exec_date: "17-10-2018",
prev_time_period: "2017-30,2017-31,2017-32",
curr_time_period: "2018-01,2018-02",
status: "Completed"
},
{
id: 5,
exec_date: "21-01-2018",
prev_time_period: "2016-01,2016-02,2016-03,2016-04",
curr_time_period: "2016-52",
status: "Pending"
},
{
id: 6,
exec_date: "17-01-2018",
prev_time_period: "2017-31,2017-32,2017-33,2017-34",
curr_time_period: "2017-52",
status: "Running"
}
];
export interface Element {
id: number;
exec_date: string;
prev_time_period: string;
curr_time_period: string;
status: string;
}
For some reason stop and cached are icons and in my visual studio i can see icons but in stackblitz I can't convert them to icons.
Just for reference stop in red color is Stop and cached in green color is re-run
Use disabled to disable the button or *ngIf to remove it from the DOM completely.
<button mat-icon-button [disabled]="isRunning" (click)="stop_exec_job()" matTooltip="Stop Executing the Job"></button>
<button mat-icon-button [disabled]="!isRunning" (click)="stop_exec_job()" matTooltip="Stop Executing the Job"></button>
Update the state based on what action was executed. Be sure to set the correct initial state as well.
isRunning: boolean = false;
stop_exec_job() {
this.isRunning = false;
}
re_run_job() {
this.isRunning = true;
}
If you want to disable buttons in your table you will have to use disabled attribute and make a check with your element status.
[disabled]="element.status === 'Completed'" // The button will be disabled if status is Completed
[disabled]="element.status === 'Running'" // Disabled if status is Running
Considering your criteria, your Stop button will look like this:
<button
[disabled]="element.status === 'Completed'"
mat-icon-button
(click)="stop_exec_job()"
matTooltip="Stop Executing the Job"
>
<!-- Edit icon for row -->
<i class="material-icons" style="color:red"> stop </i>
</button>
And your ReRun button will be like:
<button
[disabled]="element.status === 'Running'"
class="stop_btn"
mat-icon-button
color="#b71c1c"
(click)="re_run_job()"
matTooltip="Re-Run the Job"
>
<i class="material-icons" style="color:green">
cached
</i>
</button>

Angular - How to sort table data according to specific column

I have a table with 3 colums. But I want to sort the table data according to first column (Domain) alphabetically. My table html is:
<mat-table #table [dataSource]="tableData">
<ng-container cdkColumnDef="domain">
<mat-header-cell *cdkHeaderCellDef fxFlex="50%">Domain</mat-header-cell>
<mat-cell *cdkCellDef="let config" fxFlex="50%">{{config.domain}}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="disable">
<mat-header-cell *cdkHeaderCellDef fxFlex="30%">Disabled</mat-header-cell>
<mat-cell *cdkCellDef="let config" fxFlex="30%">{{config.disabled}}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="button">
<mat-header-cell *cdkHeaderCellDef fxFlex="15%"></mat-header-cell>
<mat-cell *cdkCellDef="let config" fxFlex="15%">
<mat-icon (click)="deleteDomain(config)" style="cursor: pointer">delete_forever</mat-icon>
</mat-cell>
</ng-container>
<mat-header-row *cdkHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *cdkRowDef="let config; columns: displayedColumns;"></mat-row>
</mat-table>
And my .ts file for this table is consisting following code :
private _domainData = new BehaviorSubject<Domain[]>([]);
#Input()
set domainData(value: Domain[]) {
this._domainData.next(value);
}
displayedColumns = ['domain', 'disable', 'button'];
tableData: Domain[];
Is it possible to achieve this ?
You can achieve this from the TypeScript by sort method when clicked on the particular column.
I don't think you need to write this logic in the back end.
If the intention is to just display the sorted data according to your Domain column. Then, please do at blackened that would be much better.
Hope this helps.
If you are using Angular Material, you can use Sort Header to sort data .
Or atleast you can use the logic they provided for sorting in first example.

how to slice td limit by 2 in table angular

I have a carsList array having 5 values. i try slice 2 by 2
<table>
<tr *ngFor="#item of carsList | slice:0:2; #i = index">
<td>{{i}}. {{item}}</td>
</tr>
</table>
above code split 2 values only. I want slice 2 by 2.
Example
0 1
2 3
4 5
Code:
https://plnkr.co/edit/BZnaSIldgO3Dmwao1mo6?p=preview
kindly help me . where is my mistake.
The slice pipe results an array with 2 elements and ngFor would iterate over the elements in the resulted array, that doesn't provide the expected result.
To achieve the result either you need to convert the array structure or do something like this with an additional ngIf with nested ngFor.
<table>
<ng-container *ngFor="#item of carsList; #i = index">
<tr *ngIf="i % 2 === 0">
<td *ngFor="#item1 of carsList|slice:i:i + 2; #i1 = index" class="car-title">{{i1}}. {{item1}}</td>
</tr>
</ng-container>
</table>
or by removing nested ngFor and simply creating 2 td manually.
<table>
<ng-container *ngFor="#item of carsList; #i = index">
<tr *ngIf="i %2 === 0">
<td>{{i}}. {{item}}</td>
<td>{{ i + 1 }}. {{carsList[i + 1]}}</td>
</tr>
</ng-container>
</table>
FYI : Where ng-container can be used to group element which doesn't put in the DOM tree.
Updated plunker link.
Pranav's answer almost conveyed the idea for achieving it, however his plunker seemed to not show exactly what the OP had, in addition to looking a bit over-complicated.
A simple fix as per the plunker in OP would be this (confirmed it to be working there),
<ul>
<ng-container *ngFor="#item of carsList; #i = index">
<li *ngIf="i % 2 === 0">
<span class="car-title">{{i}}. {{item}}</span>
</li>
</ng-container>
</ul>
Try this -
<ul>
<ng-container *ngFor="#item of carsList; #i = index">
<li *ngIf="i % 2 === 0">
<span class="car-title">{{i}}. {{item}}</span>
<span class="car-title">{{i+1}}. {{carsList[i + 1]}}</span>
</li>
</ng-container>
</ul>

Categories

Resources