I have a material table that updates its data using filters. One filter pulls records for invoices that are unpaid. For unpaid records, I've added a custom column that contains an input called payment_amount
. The payment_amount
field is not part of the table's DataSource
object. I don't want it to be because I'm using the input to calculate two other fields in the DataSource
. So the process is when a check comes in, I can input the payment amount, then rate
and fuel_surchage
are automatically calculated sending the updated record to the backend.
The problem is the inputs need to be in a FormArray so they have unique references for Validation and values correspond to the record's id.
I don't know where to start adding these input elements to a Form array. I know I need a FormGroup
but I don't know where to place it, and I know I need to declare a FormArray
but I'm not sure how to populate it as the table is rendered. Any Ideas?
Here's what I have thus far.
<ng-container matColumnDef="payment_amount">
<th mat-header-cell mat-sort-header *matHeaderCellDef> Payment Amount</th>
<td mat-cell *matCellDef="let shipment; let index = index">
<mat-form-field>
<mat-label>Payment Amount</mat-label>
<mat-icon inline matPrefix fontIcon="attach_money"></mat-icon>
<input matInput required minLength="300" (blur)="calculateBill(shipment.id,$event.target.value)">
</mat-form-field>
</td>
</ng-container>
<ng-container matColumnDef="shipments.rate">
<th mat-header-cell mat-sort-header *matHeaderCellDef> Rate</th>
<td mat-cell *matCellDef="let shipment"> {{shipment.rate | currency}}</td>
</ng-container>
<ng-container matColumnDef="shipments.fuel_surcharge">
<th mat-header-cell mat-sort-header *matHeaderCellDef> Fuel Surcharge</th>
<td mat-cell *matCellDef="let shipment"> {{shipment.fuel_surcharge | currency}}</td>
</ng-container>
In my component;
calculateBill(shipment_id, value){
//calculates the two fields and updates the table cells. Updating the table view works
let index = this.dataSource.shipmentsResponse.data.findIndex({
shipment => shipment.id===shipment_id
});
this.dataSource.shipments[index].fuel_surcharge = value-300;
this.dataSource.shipments[index].rate = 300;
}
You need to use formArrayName="array"
on the table and then create a form control for each row on the table, please find below a working stackblitz
html
<form [formGroup]="formGroup">
<table
mat-table
[dataSource]="dataSource"
class="mat-elevation-z8"
formArrayName="array"
>
<ng-container matColumnDef="payment_amount">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Payment Amount</th>
<td mat-cell *matCellDef="let shipment; let index = index">
<mat-form-field>
<mat-label>Payment Amount</mat-label>
<mat-icon inline matPrefix fontIcon="attach_money"></mat-icon>
<input
matInput
[formControlName]="index"
(blur)="calculateBill(shipment.id,$any($event!.target)!.value!)"
/>
</mat-form-field>
</td>
</ng-container>
<ng-container matColumnDef="rate">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Rate</th>
<td mat-cell *matCellDef="let shipment">{{shipment.rate | currency}}</td>
</ng-container>
<ng-container matColumnDef="fuel_surcharge">
<th mat-header-cell mat-sort-header *matHeaderCellDef>Fuel Surcharge</th>
<td mat-cell *matCellDef="let shipment">
{{shipment.fuel_surcharge | currency}}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayColumns"></tr>
<tr
mat-row
*matRowDef="let row; let i = index; columns: displayColumns;"
></tr>
</table>
</form>
<!-- Copyright 2023 Google LLC. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at https://angular.io/license -->
{{formGroup.value | json}}
ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { MatCommonModule } from '@angular/material/core';
import { MatTableModule } from '@angular/material/table';
import { MatIconModule } from '@angular/material/icon';
import { MatFormFieldModule } from '@angular/material/form-field';
import {
FormArray,
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
export interface PeriodicElement {
name: string;
position: number;
weight: number;
symbol: string;
}
const ELEMENT_DATA: any[] = [
{ id: 1, name: 'Hydrogen', payment_amount: 0, rate: 0, fuel_surcharge: 0 },
{ id: 2, name: 'Helium', payment_amount: 0, rate: 0, fuel_surcharge: 0 },
{ id: 3, name: 'Lithium', payment_amount: 0, rate: 0, fuel_surcharge: 0 },
{ id: 4, name: 'Beryllium', payment_amount: 0, rate: 0, fuel_surcharge: 0 },
];
/**
* @title Basic use of `<table mat-table>`
*/
@Component({
selector: 'table-basic-example',
styleUrls: ['table-basic-example.css'],
templateUrl: 'table-basic-example.html',
standalone: true,
imports: [
MatTableModule,
CommonModule,
MatIconModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
],
})
export class TableBasicExample {
formGroup: FormGroup = new FormGroup({
array: new FormArray([]),
});
displayColumns = ['payment_amount', 'rate', 'fuel_surcharge'];
dataSource = ELEMENT_DATA;
ngOnInit() {
const arrayCtrl: FormArray = <FormArray>this.formGroup.get('array');
this.dataSource.forEach((x: any) => {
arrayCtrl!.push(
new FormControl(x.payment_amount, [
Validators.required,
Validators.max(300),
])
);
});
}
calculateBill(shipment_id: any, value: any) {
//calculates the two fields and updates the table cells. Updating the table view works
let index = this.dataSource.findIndex(
(shipment: any) => shipment.id === shipment_id
);
this.dataSource[index].fuel_surcharge = value - 300;
this.dataSource[index].rate = 300;
}
}