How to implement an OnClick action on a MatTable row? - html

I want to implement an OnClick on a row with reference. If i click on a row with user 1 i want to go so see details of user 1
For example, if I click on the row of user1, I want to access the information stored in the row for this user1.
This is the html file :
<table mat-table [dataSource]="dataSource">
<ng-container matColumnDef="country">
<th mat-header-cell *matHeaderCellDef> Country </th>
<td mat-cell *matCellDef="let element"> {{element.country}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="onClick(row)" >{{row.onClickAction}}</tr>
</table>
And this is the ts file :
export class AppComponent {
onClickAction = '';
onClick(){
this.onClickAction = 'Action';
}
}

Try like this , now you will have access to row data and use router service to navigate to another component.
template
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="clickHandler(row)">
</tr>
component
export class AppComponent {
constructor(private _router: Router) {}
clickHandler(row) {
this._router.navigateByUrl(`user/${row.id}`);
console.log(row)
}
}
detail component
export class DetailComponent implements OnInit {
public selectedUserId : string;
constructor(private _activatedRoute: ActivatedRoute) {}
ngOnInit() {
this._activatedRoute.params.subscribe(params => {
this.selectedUserId = params.id;
})
}
}
demo πŸš€πŸš€
UpdatedπŸ§™β€β™‚οΈ
in case you want to send a complex object between the routes you can a shared service (singleton services) , or in case you using angular version 7.3 and above navigate accept another parameter with type NavigationExtras has a property called state accept an object later , after we navigate to the target component we can get the value of state from history object window.history.state
master
clickHandler(row) {
this._router.navigate([`details`,row.position] , {state:{...row} });
}
details
this.user = window.history.state;
you can use window.history.state or the router service to get the state value
demo β˜•β˜•

If you pass data in in the html (click)="onClick(row)" you can use that in your method:
onClick(rowData) {
console.log(rowData);
this.onClickAction = 'Action';
}

Related

Populate option set of multiple select statement using data from a table column in Angular Material

Here's the scenarios:
I have a table that displays dealer information, and one of the columns in the table is agents name.
I'm looking to create a table filter that filters the table based on agent name. For the filter I'm using a Select option set that looks like this.
UI:
HTML Code for select:
<div>
<button mat-raised-button class="filter"
(click)=select.open()
>
<mat-icon style="margin-right: 5px;">filter_list</mat-icon>
FIlter by Agent
</button>
<div class="mat-select-wrapper">
<mat-select #select [formControl]="agentsControl" multiple disableOptionCentering panelClass="myPanelClass">
<mat-option *ngFor="let agent of agentsList" [value]="agent">{{agent}}</mat-option>
</mat-select>
</div>
TS Code for Select drop down:
agentsControl = new FormControl();
agentsList: string[] = ['John Doe', 'Mary Jane', 'Johannah Kiffin', 'Eldin Astbery', 'Stephen Curry', 'Chris Smith'];
Here is the code for the table:
HTML Code:
<div flexLayout>
<mat-card>
<div class="table-container">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" aria-label="Dealer Queue table">
<ng-container matColumnDef="dealer_id">
<th scope="col" mat-header-cell *matHeaderCellDef>Dealer #</th>
<td mat-cell *matCellDef="let el"> {{el.dealer_id}} </td>
</ng-container>
<ng-container matColumnDef="dealership_name">
<th scope="col" mat-header-cell *matHeaderCellDef>Dealership</th>
<td mat-cell *matCellDef="let el"><a [routerLink]="['dreams-account-page',el.dealer_id]">{{el.dealership_name}} </a></td>
</ng-container>
<ng-container matColumnDef="agent_name">
<th scope="col" mat-header-cell *matHeaderCellDef>Agent</th>
<td mat-cell *matCellDef="let el"> {{el.agent_name}} </td>
</ng-container>
<ng-container matColumnDef="state">
<th scope="col" mat-header-cell *matHeaderCellDef>State</th>
<td mat-cell *matCellDef="let el"> {{el.state}} </td>
</ng-container>
<ng-container matColumnDef="phone_number">
<th scope="col" mat-header-cell *matHeaderCellDef>Phone</th>
<td mat-cell *matCellDef="let el"> {{el.phone_number}} </td>
</ng-container>
<ng-container matColumnDef="total_cancellations">
<th scope="col" mat-header-cell *matHeaderCellDef>Cancellations</th>
<td mat-cell *matCellDef="let el"> {{el.total_cancellations}} </td>
</ng-container>
<ng-container matColumnDef="cancellations_over_120_days">
<th scope="col" mat-header-cell *matHeaderCellDef>Cancellations > 120 Days</th>
<td mat-cell *matCellDef="let el"> {{el.cancellations_over_120_days}} </td>
</ng-container>
<ng-container matColumnDef="total_titles_outstanding">
<th scope="col" mat-header-cell *matHeaderCellDef>Titles</th>
<td mat-cell *matCellDef="let el"> {{el.total_titles_outstanding}} </td>
</ng-container>
<ng-container matColumnDef="titles_over_90_days">
<th scope="col" mat-header-cell *matHeaderCellDef>Titles > 90 Days</th>
<td mat-cell *matCellDef="let el"> {{el.titles_over_90_days}} </td>
</ng-container>
<ng-container matColumnDef="last_contact_date">
<th scope="col" mat-header-cell *matHeaderCellDef>Days Since Last Contact</th>
<td mat-cell *matCellDef="let el"> {{el.last_contact_date | daysSinceToday}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let dqrow; columns: displayedColumns" (click)="openBottomSheet(dqrow)"></tr>
</table>
</div>
<mat-paginator #paginator [pageSizeOptions]="[10, 20, 50, 100, 200, 500]" showFirstLastButtons></mat-paginator>
</mat-card>
</div>
TS for table:
data: DealerQueueTable[] = [];
displayedColumns: string[] = [
'dealer_id',
'dealership_name',
'agent_name',
'state',
'phone_number',
'total_cancellations',
'cancellations_over_120_days',
'total_titles_outstanding',
'titles_over_90_days',
'last_contact_date',
];
dataSource = new MatTableDataSource(this.data);
private subscriptionToDealerQueueTable : Subscription;
constructor(private dreamsService : DreamsService) { }
ngOnInit(): void {
this.subscriptionToDealerQueueTable = this.dreamsService.getDealerQueue().subscribe( dealerQueue => {
this.dataSource.data = dealerQueue;
})
}
#ViewChild('paginator') paginator: MatPaginator;
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
}
ngOnDestroy() {
this.subscriptionToDealerQueueTable?.unsubscribe();
}
}
Table Model Code:
export interface DealerQueueTable {
dealer_id : number,
dealership_name : string,
agent_name : string,
state : string,
phone_number : number,
total_cancellations : number,
cancellations_over_120_days : number,
total_titles_outstanding : number,
titles_over_90_days : number,
last_contact_date : Date,
market_manager : string,
market_manager_phone : string,
market_manager_email : string
}
Both are child components that share a common parent.
Here's an image of the folder structure:
So basically what I'm trying to do and can't figure out how to do is rather than populating the option set array "agentsList" manually, I'm trying to figure out how this can be done dynamically and programmatically? Where all the unique agent names present in table column agent are passed as values to the option set array.
Here's how I was able to solve this:
In the API call for the table data I retrieved the list of unique agent names and emitted it to the parent component:
Dealer Table TS:
#Output() agentData = new EventEmitter<string[]>();
ngOnInit(): void {
this.subscriptionToDealerQueueTable = this.dreamsService.getDealerQueue().subscribe( dealerQueue => {
//Set the dealerQueue value to this.unfilteredData so we can alter it and still access it later.
this.unfilteredData = dealerQueue;
//Call method to set the this.datasource.data based on given filters.
this.setDataSource(this.unfilteredData, this.FilterValues?.value || []);
let uniqueAgentList = [...new Set(dealerQueue.map(item => item.agent_name))];
this.agentData.emit(uniqueAgentList);
})
this.subscriptionToOptionSetValueChanges = this.FilterValues.valueChanges.subscribe( x => {
this.setDataSource(this.unfilteredData, x || []);
});
}
Using the spread (...) operator and Set operator in javascript we are able to get the unique set of values from the data returned by the api. I then emitted this value to the parent component.
Spread Operator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Set Operator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
Parent HomeComponent.html
<div fxFlexOffset="2%" >
<div class="element-header">Dealer Queue Table</div>
<app-filter #FilterDropdown [agentsListOptions]="agentList" ></app-filter>
<app-dealer-queue-table
*ngIf = "FilterDropdown?.agentsControl"
(agentData)="getUniqueAgentData($event)"
class="dealerqueuetable"
[FilterValues]="FilterDropdown?.agentsControl"
></app-dealer-queue-table>
</div>
Parent Home.Component.ts
export class HomeComponent implements OnInit {
agentList = [];
constructor() { }
ngOnInit(): void {
}
getUniqueAgentData(data : any) {
this.agentList = data;
}
}
The event binding, '(agentData)="getUniqueAgentData($event)', connects the event in the child, this.agentData.emit(uniqueAgentList), to the method in the parent, getUniqueAgentData(data : any) {
this.agentList = data;
}.
Now that we have a unique list of agents stored in agentList=[]; in our parent component we pass [agentsListOptions]="agentList" as in input back to the child FilterComponent.
Home.component.html
<app-filter #FilterDropdown [agentsListOptions]="agentList" ></app-filter>
Receive agentListOptions as an input in the filter component:
filter.component.ts
#Input() agentsListOptions: string[];
And finally use it to populate your dropdown options in the html
<mat-select [formControl]="agentsControl" multiple>
<mat-option *ngFor="let agent of agentsListOptions" [value]="agent">{{agent}
</mat-option>
</mat-select>

Angular Material Table - how to place *ngIf on whole column?

I have an Angular Material Table and would like to place an ngIf on the whole column so the whole column, header & data cells only show if the condition is true. I can place it on the data cell with little issue but I can't seem to work how to place it on the ng-container or on the mat-header-cell.
I have tried using ngFor and then using an ngIf, I have tried wrapping the header in a div or a table header & row but no luck.
result is the object, which has a property of websiteUrl
<table mat-table matSort [dataSource]="dataSource">
<ng-container matColumnDef="websiteUrl">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Website Link</th>
<td mat-cell *matCellDef="let result">
<div *ngIf="showWebsiteLink===1">
<div *ngIf="websiteLinkVisible(result)">
<a (click)="copyLink(result.websiteUrl)">
Copy Link
</a>
</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
I made a very simple example for you. To remove the entire col you would need to use *ngIf to remove it from the template when someCondition is false. And also you would need to remove the same col name from displayedColumns array. The latter is more important. In the example I have removed col 'weight'.
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 5';
displayedColumns = ['position', 'name', 'weight', 'symbol'];
dataSource = new MatTableDataSource<Element>(ELEMENT_DATA);
someCondition: boolean = false;
constructor() {
if (!this.someCondition) {
this.displayedColumns.splice(2,1);
}
}
}
https://stackblitz.com/edit/angular-material-table-data-source-kdttsz?file=app%2Fapp.component.ts

HTML Angular Material: Hide Columns in a dynamically forming table

I want to hide columns in my table based on an ID value that is sent to the page upon opening it. I've seen there is a method for tables whose columns are formed as needed... https://github.com/angular/components/issues/9940 this post outlines that. However my table is formed as such.
<table mat-table [dataSource]="dataSource" align="center" class="mat-elevation-z8">
<ng-container [matColumnDef]="column" *ngFor="let column of getDisplayedColumns()">
<th mat-header-cell *matHeaderCellDef > {{column}} </th>
<td mat-cell *matCellDef="let element" > {{element[column]}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns" ></tr>
<tr mat-row *matRowDef="let row; columns: getDisplayedColumns();" ></tr>
</table>
getDisplayedColumns() should return my array with only the elements I want to show
I'm confused on how I would implement the above method into my code given that my table is formed differently than the OP's.
Probably a lot of ways to do this, but I had a similar need so created a component and passed in all possible columns. The component does a lot of special formatting like booleans = "yes" | "no" or dates dates | date:'MM/dd/yyyy h:mm:ss a', but for your case isVisible is what you seem to need. A few key pieces:
export interface IPaginateRowAttributes {
attributeName: string;
listLabel: string;
listFormat: string;
isAttribute: boolean;
lookupIndex: number;
isVisible: boolean;
}
#Input() rowColumns: IPaginateRowAttributes [] = [];
#Input() yourId
<tr *ngFor="let dynamicListRowAttribute of dynamicListRowAttributes">
<ng-container *ngIf="dynamicListRowAttribute.listFormat=='number' && dynamicListRowAttribute.isVisible==true" matColumnDef={{dynamicListRowAttribute.compositeKey}}>
<th mat-header-cell *matHeaderCellDef mat-sort-header (click)="sort(dynamicListRowAttribute.attributeName, true, dynamicListRowAttribute.listFormat)"> {{dynamicListRowAttribute.listLabel}} </th>
<td mat-cell *matCellDef="let element"> {{element['attributes'][dynamicListRowAttribute.attributeName] | currency}} </td>
</ng-container>
<ng-container> more format options</ng-container>
ngOnChanges(changes: SimpleChanges) {
let returnHeaders: string[] = [];
for (let detail of displayColumns) {
returnHeaders.push(detail.dictionaryCompositeKey);
}
}
So right there include ngOnChanges, you could check your ID value and set isVisible per column based on whatever criteria you want to use and let the *ngIf filter the rest. This is one option anyway, there may be better solutions in terms of performance.

Angular Data table, dataStream.pipe is not a function

I'm trying to create a table using angular data table, but I'm getting this error which I think comes from the dataSource, but I can't see how to solve it. Any suggestions is appreciated it.
This the error I get:
dataStream.pipe is not a function
Here is the html for the table:
<div class="example-container mat-elevation-z8">
<mat-table #table [dataSource]="dataSource">
<!--*- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!--* Idr Column -->
<ng-container matColumnDef="idr">
<mat-header-cell *matHeaderCellDef> Idr </mat-header-cell>
<mat-cell *matCellDef="let receipts"> {{receipts.idr}} </mat-cell>
</ng-container>
<!--* Period Start Column -->
<ng-container matColumnDef="fromDate">
<mat-header-cell *matHeaderCellDef> Periode start </mat-header-cell>
<mat-cell *matCellDef="let receipts"> {{receipts.fromDate}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumnsReceipt"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumnsReceipt;"></mat-row>
</mat-table>
</div>
This is the dataSource class:
export class ReceiptDataSource extends DataSource<any> {
constructor(private registerService: RegisterService) {
super();
}
connect(): Observable<IReceipt[]> {
return this.registerService.getReceipt();
}
disconnect() {}
}
This is the part of my component where the dataSource and table structure is defined:
dataSource = new ReceiptDataSource(this.registerService);
displayedColumnsReceipt = ['idr', 'fromDate'];
And this is the service where the getReceipt method is declared:
getReceipt(): Observable<IReceipt[]> {
console.log('sdrParsed: ', this.sdrParsed.receipts);
return this.sdrParsed.receipts;
}
It is difficult to know for sure from the code sample provided, but given the error is saying .pipe is not a function, it almost certainly means the return from your connect method is NOT an Observable. You could try some code to see if you can subscribe to the return of your service function to confirm that subscribe also fails. If it turns out to be not an Observable you can make sure to return a valid Observable object.

Not able to Retrieve data from firebase using keys in angular 2

Hi I want to iterate the data of firebase and need to display it in table.
Below is my firebase structure.I want to display the bill to, email id and po number in the tables.i can see the data in console.log, but its not populating in tables.
EDI855
Bill To
-L9ac7clRzSVT-EfGxYv:
"123456789"
-L9acDp2k34qDpubJFr6:
"123456780"
Email Id
-L9ac7cxYSALI3Ogj-nt:
"test#gmail.com"
-L9acDp87NO83OQutasK:
"test1#gmail.com"
Po Number
-L9ac7cvtNNzg7hYa355:
"123456789"
-L9acDp4PPOSo9VL9ysB:
"VV002"
Below is my html table:
div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>No</th>
<th>Bill To</th>
<th>Requested by</th>
<th>Po Number</th>
</tr>
</thead>
<tbody>
<tr>>
<td *ngFor="let x of items">{{x.$value}}</td>
</tr>
</tbody>
</table>
Below is my firebase code:
constructor(public af: AngularFire) {
af.database.list('/EDI855').subscribe(x =>{
this.items=x;
console.log(this.items)
}
Try implementing your query in the init event instead
export class MyComponent implements OnInit {
items;
constructor(public af: AngularFire) {
}
ngOnInit() {
this.af.database.list('/EDI855').subscribe(x =>{
this.items=x;
}
}
Or even better, to get the most out of the real-time database, you should consider using the async pipe. When you do that, your app will react to any changes on your data and immediately refresh the UI.
<td *ngFor="let x of items | async">{{x.$value}}</td>
Just that in this case, remember that you're changing the type of items (it is no longer the list of items, but an observable of items), than therefore, no need to subscribe to it. The async pipe will do the work.
constructor(public af: AngularFire) {
this.items = af.database.list('/EDI855');
}