I am trying to render a table in html that is the representation of a reactive form array.
I loop over the complex object (FormGroup > FormArray > FormGroup > FormArray) With the final array being the data that we want to represent to the UI in its transformed format. (Happens in the function groupFormGroup())
Everything works fine, the data is correctly transformed and displayed.
However, now I am trying to add inputs (NOT formControlName inputs) to the table and when they load, they load without me being able to click into, or change their value. They are not disabled, just unclickable. No errors come up in the console.
What I have tried:
Modifying the class names so it is no longer class='form-control'
Increasing the z-index of inputs to be on the forefront
Making the inputs position relative
Here is the code:
<div class="row" [formGroup]="address">
<div class="col-12">
<table class="w-100">
<thead>
<th></th>
<th>Order ID</th>
<th>GP Code</th>
<th>Product</th>
<th>Quantity</th>
<th>Preset Suite Number</th>
<th></th>
</thead>
<tbody>
<ng-template formArrayName="addressOrders" ngFor let-addressOrder [ngForOf]="address.get('addressOrders').controls" let-ao="index">
<ng-container [formGroup]="addressOrder">
<ng-template ngFor let-fulfillment [ngForOf]="groupFormGroup(addressOrder.get('fulfillments').getRawValue(),'orderDetailId')" let-f="index">
<tr>
<td class='table-input'>
<input type="checkbox"
(change)="updateFulfillmentFormControl(fulfillment,addressOrder,'selected',$event)"/>
</td>
<td>{{addressOrder.get('orderId').value}}</td>
<td>{{fulfillment['gpItemNumber']}}</td>
<td>{{fulfillment['name']}}</td>
<td>{{fulfillment['quantity']}}</td>
<td>
<ng-container *ngIf="fulfillment['isSuite']">
<!-- <input class="form-control" (keydown)="updateFulfillmentFormControl(fulfillment,addressOrder,'suiteNumber',$event)" /> -->
<input type="text" class="form-control" />
</ng-container>
</td>
<td>Edit Detail</td>
</tr>
</ng-template>
</ng-container>
</ng-template>
</tbody>
</table>
</div>
</div>
And the code generating the Form Group:
draw() {
this.formGroup = this.initFormGroup(this.ordersToDisplay)
}
initFormGroup(ordersToDisplay: Array<BulkFulfillCustomer>):FormGroup {
var queueList = this.queues.map(q => q['queue_id']);
return this.fb.group({
customers: this.fb.array(
ordersToDisplay.map(otd => {
return this.fb.group({
gpCustomerNumber:[otd.gpCustomerNumber],
customerName:[otd.customerName],
customerAddresses: this.initAddressForm(otd.addressOrders, queueList)
})
})
)
})
}
initAddressForm(addressOrders: Array<BulkFulfillAddress>, queues:Array<string>):FormArray {
return this.fb.array(
addressOrders.map(ao => {
return this.fb.group({
addressLine:[ao.addressLine],
bundleShipment:[true],
addressOrders: this.initAddressOrdersForm(ao.orders,queues)
})
})
)
}
initAddressOrdersForm(orders: Array<BulkFulfillOrder>, queues:Array<string>):FormArray {
return this.fb.array(
orders.map(o => {
return this.fb.group({
orderId: [o.order.id],
fulfillments: this.initFulfillmentsForm(o.orderDetailFulfillments,queues)
})
})
)
}
initFulfillmentsForm(fulfillments: Array<FulfillmentLine>, queues:Array<string>) {
return this.fb.array(
fulfillments.map(f => {
return this.fb.group({
selected:[true],
expandDetail:[false],
isSuite:[f.inventory.isSuite],
suitePrefix:[f.inventory.suitePrefix],
gpItemNumber:[f.inventory.gpItemNumber],
name:[f.inventory.name],
queueId:[{value:f.inventory.pulseQueueId,disabled:(!this.isPulseFulfill && f.inventory.fulfillmentMethod != INVENTORY_FULFILLMENT_PULSE)}, Validators.compose([Validators.required, ContainsListValidator(queues,'Queue ID')])],
orderDetailId:[f.orderDetailFulfillment.orderDetailId],
pulseOrderId:[f.orderDetailFulfillment.pulseOrderId],
suiteNumber:[{value:f.orderDetailFulfillment.suiteNumber,disabled:!this.isPulseFulfill || !f.inventory.isSuite}, Validators.required],
quantity:[{value:f.orderDetailFulfillment.quantity,disabled:f.orderDetailFulfillment.fulfillmentMethod != 1},Validators.compose([Validators.required, Validators.min(1),Validators.max(f.orderDetailFulfillment.quantity)])]
})
})
)
}
Related
noticed a very strange behaviour from table body - it disappears on the first time when I click on checkboxes. As you will see in the gif, data is fetched successfully without any issues. I'm manipulating it whenever user tries to filter it. Couldn't find anywhere related issues to checkboxes and tables. Thanks for any ideas/advices! Want to mark that there are no errors in console, everything is clean, just inside the tbody gets empty
gif
<aside class="last-updates">
<div>
<span>Last 10 minutes update</span>
<div class="checkboxes">
<input type="checkbox" id="available" value="Available" v-model="needToFilter" #click="filterRoutes = true" />
<label for="available">Available</label>
<input type="checkbox" id="in-progress" value="In progress" v-model="needToFilter" #click="filterRoutes = true" />
<label for="in-progress">In progress</label>
<input type="checkbox" id="completed" value="Completed" v-model="needToFilter" #click="filterRoutes = true" />
<label for="completed">Completed</label>
</div>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">Driver ID</th>
<th scope="col">Route</th>
<th scope="col">Latitude</th>
<th scope="col">Longitude</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody v-if="routes">
<tr v-for="route in filteredRoutes" :key="route">
<th>
{{ route.driver_id }}
</th>
<th>
{{ route.route_name ? route.route_name : 'No data' }}
</th>
<th>
{{ route.route_latitude }}
</th>
<th>
{{ route.route_longitude }}
</th>
<th
:class="
route.route_status === 'Available'
? 'not-driving'
: route.route_status === 'In progress'
? 'in-progress'
: 'completed'
"
>
{{ route.route_status }}
</th>
</tr>
</tbody>
<tbody v-else>
<p>Fetching data from database...</p>
</tbody>
</table>
</aside>
<script>
import { ref, computed } from 'vue';
import getAllRoutes from '../../composables/routes/getAllRoutes';
export default {
async setup() {
let routes = ref([]);
let needToFilter = ref([]);
let filterRoutes = ref(false);
routes = await getAllRoutes();
const filteredRoutes = computed(() => {
console.log(routes);
if (filterRoutes.value) {
if (needToFilter.value.length === 1) {
return routes.filter(route => route.route_status.startsWith(needToFilter.value[0]));
} else if (needToFilter.value.length > 1) {
for (let i = 0; i < needToFilter.value.length; i++) {
// todo
}
}
} else {
// Default sorting, displayed when page is loaded
const sortingDefaultPriority = ['Available', 'In progress', 'Completed'];
return routes.sort((a, b) => sortingDefaultPriority.indexOf(a.route_status) - sortingDefaultPriority.indexOf(b.route_status));
}
});
return { routes, needToFilter, filteredRoutes, filterRoutes };
}
};
</script>
The issue was with #click. For some reasons whenever I click the checkbox it removes the content from tbody tag. Removing the #click helped me out, found another way to watch the click.
I am using custom filters and ngx-pagination on my JSON data. The problem here is when I am filtering the data my pagination control is not getting updated, still showing the old page size and allowing pagination even if the data is not available. Please suggest.
Right now, it has 21 results without any filter applied
Now, I have applied filters n now it's showing only 1 record but pagination is still showing 5 pages (considering 5 per page size).
table-filter.pipe.ts
import { Pipe, PipeTransform } from "#angular/core";
#Pipe({
name: "tableFilter"
})
export class TableFilterPipe implements PipeTransform {
transform(list: any[], filters: any) {
const keys = Object.keys(filters).filter(key => filters[key]);
const filterUser = (user: { [x: string]: any; }) =>
keys.every(key => {
if (key == "sdob") {
return new Date(user["dob"]) >= new Date(filters[key]);
} else if (key == "edob") {
return new Date(filters[key]) >= new Date(user["dob"]);
} else {
return user[key] === filters[key];
}
});
return keys.length ? list.filter(filterUser) : list;
}
}
app.component.html
<table class="table table-sm table-responsive">
<thead>
<tr>
<th></th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Gender</th>
<th>DOB (mm/dd/yyyy)</th>
<th>Impact</th>
<th>Score</th>
<th></th>
</tr>
</thead>
<tbody>
<tr class="record-row" (click)="viewUser(user)" *ngFor="let user of allUser | tableFilter: form.value | paginate: { id: 'listing_pagination', itemsPerPage: 5, currentPage: page, totalItems: totalRecords }">
<td><input *ngIf="!isEdit" [(ngModel)]="user.checked" type="checkbox" (change)="checkboxClicked()"></td>
<td>{{user.first_name}}</td>
<td>{{user.last_name}}</td>
<td>{{user.email}}</td>
<td>{{user.gender}}</td>
<td>{{user.dob}}</td>
<td>{{user.impact}}</td>
<td>
<div [ngClass]="getClass(user)">{{user.score}}</div>
</td>
<td>
<button *ngIf="!isEdit" class="btn btn-primary btn-sm" (click)="editUser(user)">Edit</button>
<button class="btn btn-primary btn-sm btn-sm ml-2" (click)="deleteUser(user)">Delete</button>
</td>
</tr>
</tbody>
</table>
<pagination-controls id="listing_pagination" directionLinks="true" (pageChange)="page = $event"></pagination-controls>
I have a table containing some rows.The table data comes from loop.Here I need to add new columns based on selection of checkbox.Suppose I checked product,a new table heading will create as product along with blank rows just below the table heading,again If I uncheck created columns will be removed.New columns should create before add button.Here is the code below
https://stackblitz.com/edit/angular-table-focus
app.component.html
<div class="container">
<h2>Basic Table</h2>
<p>The .table class adds basic styling (light padding and only horizontal dividers) to a table:</p>
<div><button (click)="enable()">Edit</button> <input type="checkbox" value="product">Product <input type="checkbox" value="market">market</div>
<table class="table border">
<thead>
<tr>
<th>Items</th>
<th>Business</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<td> <input #focus [disabled]='toggleButton' type="text" value="{{row.name}}"> </td>
<td> <input [disabled]='toggleButton' type="text" value="{{row.items}}"> </td>
<td> <button (click)="addRow(i)">Add</button></td>
</tr>
</tbody>
</table>
</div>
app.component.ts
import { Component,ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedRow : Number;
#ViewChild('focus') input: ElementRef;
public toggleButton: boolean = true;
ngOnInit() {
}
groups=[
{
"name": "pencils",
"items": "red pencil"
},
{
"name": "rubbers",
"items": "big rubber"
},
{
"name": "rubbers1",
"items": "big rubber1"
},
];
addRow(index): void {
var currentElement = this.groups[index];
this.groups.splice(index, 0, currentElement);
}
enable(){
this.toggleButton = false
setTimeout(()=>{ // this will make the execution after the above boolean has changed
this.input.nativeElement.focus();
this.selectedRow = 0;
},0);
}
setClickedRow(index) {
this.selectedRow = index;
}
}
Check out my StackBlitz for live demo: https://stackblitz.com/edit/angular-table-focus-77xrnn
To add or remove columns dynamically, we have to first fetch the values from the component instead of hard coding them in the template.
columns = ["Items", "Business"];
Component:
And whenever a column checkbox is toggled, we listen to the change event and add or delete the column accordingly.
onColumnStatusChange(column, isChecked) {
if (isChecked) {
// Add column to the table.
this.columns.push(column);
this.groups.forEach(value => {
value[column] = "";
});
} else {
// Delete column from the table.
this.columns.splice(this.columns.indexOf(column), 1);
this.groups.forEach(value => {
delete value[column];
});
}
}
Template:
In the template, columns and also table row items must be fetched dynamically.
<input type="checkbox" value="product" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">Product
<input type="checkbox" value="market" (change)="onColumnStatusChange($event.target.value, $event.target.checked)">market
...
...
<tr>
<ng-container *ngFor="let column of columns; let i = index">
<th>{{ column }}</th>
</ng-container>
</tr>
...
...
<tr *ngFor="let row of groups;let i = index" (click)="setClickedRow(i)" [class.active]="i == selectedRow">
<ng-container *ngFor="let item of objectKeys(row); let j = index">
<td><input #focus [disabled]='toggleButton' type="text" value="{{row[item]}}"></td>
</ng-container>
<td><button (click)="addRow(i)">Add</button></td>
</tr>
I use Angular 6, I get data from a JSON file and I display it in a table. I would like to be able to check the checkboxes in order to be able to carry out POST processing later for checkbox checked (so true).
The problem is that I get dynamic values so I have to be able to retrieve the value of a checkbox (true or false) even if the JSON file changes (adding data).
I can not get the value of checkbox (true or false) dynamically...
Can you help me please.
demande-ouverture-compte.component.html :
<h2>Demandes d'ouverture de compte</h2>
<hr>
<form #form = "ngForm" (ngSubmit) = "getVerificationCheckboxValidation(form)" [formGroup]="profileForm">
<table class="table table-striped table-dark">
<thead>
<tr>
<th scope="col">Nom</th>
<th scope="col">N° demande</th>
<th scope="col">date</th>
<th scope="col">Validation</th>
</tr>
</thead>
<tbody *ngFor="let demande of demandes; let i = index">
<tr>
<th scope="row">{{ demande.nom }}</th>
<td>{{ demande.id_demande }}</td>
<td>{{ demande.date }}</td>
<td>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" name="test" id="customCheck{{ i }}" [checked]="demande.checked">
<label class="custom-control-label" for="customCheck{{ i }}">Valider la demande d'ouverture</label>
</div>
</td>
</tr>
</tbody>
</table>
<table>
<tr *ngFor="let demande of demandes; let j = index">
<td>
<input type="checkbox" [checked]="demande.checked" formControlName="hello" id="{{demande.roleID}}" />
</td>
<td>
</td>
<td>Voici demandes{{ j }}</td>
</tr>
</table>
<button class="col-2 offset-10 addAccount">Valider</button>
</form>
<!-- Remplacer le bouton valider par cette image -->
<!--<img src="../../assets/img/addAccount.svg" alt="" class="col-4 offset-7 addAccount">-->
demande-ouverture-compte.component.ts :
import { Component, OnInit } from '#angular/core';
import { DemandeOuvertureCompteService } from '../demande-ouverture-compte.service';
import { FormGroup, FormControl, NgForm } from '#angular/forms';
#Component({
selector: 'app-demande-ouverture-de-compte',
templateUrl: './demande-ouverture-de-compte.component.html',
styleUrls: ['./demande-ouverture-de-compte.component.scss']
})
export class DemandeOuvertureDeCompteComponent implements OnInit {
demandes: Object;
profileForm = new FormGroup({
hello: new FormControl('')
});
playerName: string;
constructor(private demandeOuvertureCompte: DemandeOuvertureCompteService) { }
ngOnInit() {
this.demandeOuvertureCompte.getDemandesOuvertureCompte().subscribe(
demandeOuvertureCompte => this.demandes = demandeOuvertureCompte
);
}
getVerificationCheckboxValidation(form: NgForm) {
console.log(form.value.test);
console.log(this.profileForm.value);
console.log("test");
console.log(this.playerName);
return this.playerName;
}
}
You can look for the valueChanges on FormGroup. Something like
ngOnInit() {
this.profileForm.valueChanges.subscribe(
value => console.log(value);
)
I have a FormArray where I am passing an IsEditMode:boolean flag to toggle the view from Edit Mode to Read-Only mode.
When I change the value of IsEditMode, I am getting error
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: false'. Current value: 'ngIf: true'
I tried looking for an example but was not able to find anything. Please suggest if there is a better way to do this as well.
createForm(): void {
this.itemForm = this.fb.group({
items: this.fb.array([])
});
}
createItem(jp: Item & EditMode): FormGroup {
return this.fb.group({
id: jp.id,
profileId: jp.profileId,
name: [jp.name, Validators.required],
isEditMode: [jp.isEditMode]
});
}
addNewItem() {
this.items = this.jobPreferenceForm.get(
'items'
) as FormArray;
this.items.push(
this.crateItem({
id: 0,
profileId: '',
name: '',
isEditMode: true
})
);
}
<table class="table table-condensed table-striped table-hover">
<tbody>
<tr formArrayName="items" *ngFor="let item of itemForm.get('items').controls; let i = index;">
<td>
<span *ngIf="!item.value.isEditMode">{{item.value.name}}</span>
<div *ngIf="item.value.isEditMode" [formGroupName]="i">
<input type="text" class="form-control" formControlName="name">
</div>
</td>
<td>
<a *ngIf="!item.value.isEditMode" (click)="item.value.isEditMode = !item.value.isEditMode">Edit</a>
<a *ngIf="item.value.isEditMode" class="text-muted mr-2" [attr.disabled]="!item.valid" (click)="cancel(item)">Cancel</a>
<a *ngIf="item.value.isEditMode" class="text-success mr-2" (click)="save(item)">Save</a>
<a *ngIf="item.value.isEditMode" class="text-danger">Delete</a>
</td>
</tr>
</tbody>
</table>