I'm working on a mat-stepper structure, in order to create the entity "Rental Contracts". I can create a new rental contract starting from zero, or open a saved draft and continue filling data and saving it to the database usinn entity framework API's.
One rental contract can refer to n vehicles, that the customer will use during the contract validity period.
Each vehicle is represented in a formArray element. Each vehicle can be owned by the rental house, or subleased, and have a number of properties to be filled by the user. Almost all these properties are working well, except the checkbox.
I've created a mat-checkbox to show if the selected machine is subleased or not. I need three different behaviours:
- When I create an empty element for the formArray, I need to set "false" value to the checkbox.
- Once the user selects a machine code, the system checks if it is subleased, and sets the checkbox selection to "true" ("or "false" otherwise).
- If I loaded data from draft, the checkbox needs to be set depending on what I previously saved on the db.
If I use formControl name, I am not able to manage values coming from API. If I use ngModel, I can read from DB but formArray does not work properly when I add a new element to it.
What I need is to understand how to configure my formArray element in order to get working all of the needed behaviours.
My form looks like this:
Here is my "mixed" code (I have tried several changes and commented some code that was not working):
component.ts:
[...]
vehiclesForm = new FormGroup({
stocks: new FormArray([])
});
[...]
ngOnInit(): void {
this.vehiclesForm = this.formBuilder.group({
stocks: this.formBuilder.array([])
});
}
loadData(contractData: any) {
this.contractData = contractData;
//console.log('contract-vehicles - contractData', contractData);
this.getAllBuildingSitesByCustomerId(contractData.customerId);
this.rentalTypeId = contractData.rentalTypeId;
this.secondRentalTypeId = contractData.secondRentalTypeId;
//console.log("this.contractData.rentalStocks", this.contractData.rentalStocks);
if ((<FormArray>this.vehiclesForm.get("stocks")).length === 0) {
if (this.contractData.rentalStocks != null && this.contractData.rentalStocks?.length > 0) {
this.getRentalStocksDetail(this.contractData.rentalStocks).subscribe(
(stockResults) => {
this.contractData.rentalStocks.forEach((rentalStock, index) => {
const stock = stockResults[index];
const stockIdControl = new FormControl(stock, autocompleteObjectValidator());
this.filteredStocks[index] = stockIdControl.valueChanges.pipe(
debounceTime(300),
switchMap((value) => this.getStocks(value, index))
);
this.addStockToFormArray(rentalStock, stockIdControl);
this.onStockSelectionChange(stock, index);
});
},
(err) => {
console.log("contract-vehicles.component: getRentalStocksDetail:", err);
},
);
} else {
this.addNewStockToFormArray();
}
}
}
getRentalStocksDetail(stocks: any[]): Observable<any[]> {
let calls = [];
stocks.forEach(stock => { calls.push(this.stockService.getStock(stock.stockId, true)); });
return forkJoin(calls);
}
getControls() {
//console.log("<FormArray>this.vehiclesForm.get('stocks')).controls",(<FormArray>this.vehiclesForm.get("stocks")).controls);
return (<FormArray>this.vehiclesForm['controls'].stocks['controls']);
}
getStocks(filter: any, index: number): Observable<any[]> {
if (filter?.length < 2 || typeof filter !== 'string') {
return of([]);
}
this.queryStock.find = filter;
this.queryStock.startDate = moment(((<FormGroup>(<FormArray>this.vehiclesForm.get("stocks")).controls[index])).controls.startDate.value).format('YYYY-MM-DDT00:00:00');
this.queryStock.endDate = moment(((<FormGroup>(<FormArray>this.vehiclesForm.get("stocks")).controls[index])).controls.endDate.value).format('YYYY-MM-DDT23:59:59');
return this.rentalStockService.getAvailableRentalStocks(this.queryStock).pipe(
map((response) => response),
tap((response: any[]) => {
return response.sort((a, b) => {
if (a.ownerRef === b.ownerRef) {
return a.registrationNumber < b.registrationNumber ? -1 : 1
} else {
return a.ownerRef < b.ownerRef ? -1 : 1
}
});
})
);
}
displayStock(stock: any): string {
var desc: string;
if (stock && stock.id) {
desc = stock.registrationNumber;
//console.log("stock.ownerRef", stock.ownerRef);
if (stock.ownerRef) {
desc = stock.ownerRef + ' - ' + desc;
}
}
return desc;
}
addNewStockToFormArray() {
if (!(<FormArray>this.vehiclesForm.get('stocks')).valid) {
return;
}
const formLength = (<FormArray>this.vehiclesForm.get('stocks')).length;
const stockIdControl = new FormControl(null, autocompleteObjectValidator());
this.filteredStocks[formLength] = stockIdControl.valueChanges.pipe(
debounceTime(300),
switchMap((value) => this.getStocks(value, formLength))
);
this.addStockToFormArray(null, stockIdControl);
}
public addStockToFormArray(rentalStock: any, stockControl: FormControl) {
console.log("rentalStock", rentalStock);
console.log("subleased", rentalStock?.subleased);
const checkDefaultValues = <FormGroup>(<FormArray>this.vehiclesForm.get("stocks")).controls[0];
(<FormArray>this.vehiclesForm.get("stocks")).push(
new FormGroup({
stockId: new FormControl(rentalStock ? rentalStock.id : 0),
startDate: new FormControl(rentalStock?.startDate ?? this.contractData.startDate),
endDate: new FormControl(rentalStock?.endDate ?? this.contractData.expiryDate),
stock: stockControl,
//subleased: new FormControl({value: rentalStock ? rentalStock.subleased : false, disabled: true}),
})
);
}
onStockSelectionChange(myStock: any, j: number, fromUI: boolean = false) {
console.log("onStockSelectionChange", fromUI);
let formArray = (<FormArray>this.vehiclesForm.get("stocks")).controls;
formArray[j].get('stockId').setValue(myStock.id);
//formArray[j].get('subleased').setValue(myStock.subleased);
if (fromUI) {
this.contractData.rentalStocks[j].subleased = myStock.subleased;
}
}
HTML:
<form [formGroup]="vehiclesForm">
<table style="width: 50%;">
<thead>
<tr>
<th style="text-align: center;">StartDate</th>
<th style="text-align: center;">EndDate</th>
<th style="text-align: center;">Stock</th>
<th style="text-align: center;">Subleased</th>
<th style="text-align: center;">Delete</th>
</tr>
</thead>
<tbody formArrayName="stocks">
<tr *ngFor="let stock of getControls(); let i = index" [formGroupName]="i">
<!-- Start Date -->
<td style="text-align: center;">
<div class="hidden">
<input type="text" class="form-control" formControlName="stockId">
</div>
<mat-form-field appearance="outline" class="field-width-120 w-100-p mb-12">
<mat-label></mat-label>
<input matInput [matDatepicker]="startDatePicker"
formControlName="startDate"
[min]="contractData.startDate" [max]="contractData.expiryDate">
<mat-datepicker-toggle matSuffix [for]="startDatePicker">
</mat-datepicker-toggle>
<mat-datepicker #startDatePicker></mat-datepicker>
</mat-form-field>
</td>
<!-- End Date -->
<td style="text-align: center;">
<mat-form-field appearance="outline" class="field-width-120 w-100-p mb-12">
<mat-label></mat-label>
<input matInput [matDatepicker]="endDatePicker"
formControlName="endDate"
[min]="contractData.startDate" [max]="contractData.expiryDate">
<mat-datepicker-toggle matSuffix [for]="endDatePicker">
</mat-datepicker-toggle>
<mat-datepicker #endDatePicker></mat-datepicker>
</mat-form-field>
</td>
<!-- Stock -->
<td style="text-align: center;">
<mat-form-field appearance="outline" class="field-width-stock w-100-p mb-12">
<mat-label></mat-label>
<input matInput formControlName="stock" [value]="stock"
[matAutocomplete]="autoStock" type="text" autocomplete="off">
<mat-autocomplete #autoStock="matAutocomplete" [displayWith]="displayStock">
<mat-option *ngFor="let myStock of filteredStocks[i] | async"
[value]="myStock" (onSelectionChange)="onStockSelectionChange(myStock, i, true)">
</mat-option>
</mat-autocomplete>
</mat-form-field>
</td>
<!-- Subleased -->
<td style="vertical-align: initial; text-align: center;">
<mat-checkbox class="h5" [ngModel]="contractData.rentalStocks[i]?.subleased" [ngModelOptions]="{standalone: true}" disabled></mat-checkbox>
</td>
<!-- Delete -->
<td style="vertical-align: initial; text-align: center;">
<button mat-icon-button color="warn"
(click)="onDeleteItem(i); $event.preventDefault()">
<mat-icon>delete</mat-icon>
</button>
</td>
</tr>
<tr>
<button mat-raised-button class="header-button mt-24 mt-md-0"
(click)="addNewStockToFormArray(); $event.preventDefault()">
<mat-icon>add</mat-icon>
</button>
</tr>
</tbody>
</table>
</form>
Any suggestion/help will be much appreciated!
Many thanks and best regards,
Laura
Aucun commentaire:
Enregistrer un commentaire