I am trying to create the PrimeNg editable table with angular forms. I don't understand how to use formArray. I tried so many times but I was struck with formGroupName error.
This is current behavior
※ Actual behavior ⇒ only touched and invalid component(Input field) border should output in red color.
※ I think here we should use formArray to check the validation.
※ Add Row button in another component.
HTML
<p-table [loading]="loading" styleClass="text-sm " [ngClass]="{'p-datatable-is-loading': loading}" [value]="dataSource"
[scrollable]="true" scrollHeight="calc(100% + 2px)" scrollDirection="both">
<ng-template pTemplate="header">
<tr>
<th style="width: 250px;">Project</th>
<th style="width: 250px;">Process</th>
<th style="width: 200px;">Working Hours</th>
<th style="width: 168px;">Billed To</th>
<th style="width: 350px;">What did you work on?</th>
<th style="width: 350px;">Blockers</th>
<th alignFrozen="right" pFrozenColumn class="p-frozen-column p-frozen-border-left" style="width: 114px;"></th>
</tr>
</ng-template>
<ng-template pTemplate="body" [formGroup]="_formHelper.form" let-source let-index="rowIndex">
<tr *ngIf="dataSource[index].editing; else viewingMode;" class="editing editing-mode">
<td style="width: 250px; overflow: visible">
<p-autoComplete appendTo="body" #project name="project" [(ngModel)]="dataSource[index].project_id" [suggestions]="projectSuggestions"
field="name" (onSelect)="handleSelectProject($event, index)"
(onBlur)="_formHelper.validateControl('project')" formControlName="project"
(completeMethod)="handleSearchProject($event, index)" (onFocus)="project.show()" placeholder="Search project">
</p-autoComplete>
</td>
</tr>
</ng-template>
</p-table>
TS
export class DailyReportRegularTableComponent extends DailyReportLog implements OnInit {
helper!: HelperService
_formHelper: FormService = new FormService;
_model: UIProjectLogForm = {
id: '',
process: '',
minutes: undefined,
hour: undefined,
work_note: '',
blocker_note: '',
daily_time_log_id: '',
billed_to: '',
project_id: '',
project_name: '',
};
constructor(private _helper: HelperService,private formBuilder: FormBuilder) {
super();
this.helper = this._helper;
this.initForm();
}
initForm() {
this._formHelper.form = this.formBuilder.group({
project: new FormControl(this._model.project_id, {
validators: Validators.required,
updateOn: 'blur'
})
});
}
}
Related
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 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)])]
})
})
)
}
i'm currently working on displaying large amounts of data in tables for a scientific project. i have timeStamps of experiments, which look like this:
interface TimeData {
time: string;
data: {SD: string, SEM: string, N: string, MEAN: string};
}
i have a total of 11 timeStamps in the table, all described by their 'time', e.g. 15, 30, 60, 90...
if an experiment is missing a timestamp completely or has no data for the timestamp 30 at e.g. MEAN, i want to print -- in the td.
i can't seem to wrap my head around how to display all mean data in a row and just replacing missing one's with a '--'...
i've tried wrapping in ng-container, several ngFor loops, yet it always comes out wrong, i.e. i get too many -- or none at all and my data is being displayed in the wrong td.
here's the html
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Function</th>
<th scope="col" class="text-center"
*ngFor="let timeStamp of maxTimeStamps; let i=index">t{{i + 1}}
{{timeStamp.time}}</th>
<th>ΔAUC</th>
</tr>
</thead>
<tbody *ngFor="let experiment of report.experiments; let i = index">
<tr>
<td rowspan="4" [ngStyle]="{color: experimentColors[i]}">{{experiment.code.toUpperCase()}}</td>
<td>mean</td>
------missing td goes here-------
----------------------------------
</tr>
</tbody>
</table>
Welcome to StackOverflow, If I get it right you want to show your data in rows but you add ngFor in the wrong place for that (it shouldn't add to tbody tag)! please check the corrected code below.
You can change or make a condition by ngIf if you don't want to add that td.
Good Luck.
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Function</th>
<th scope="col" class="text-center"
*ngFor="let timeStamp of maxTimeStamps; let i=index">t{{i + 1}}
{{timeStamp.time}}</th>
<th>ΔAUC</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let experiment of report.experiments; let i = index">
<td rowspan="4" [ngStyle]="{color: experiment.Colors[i]}" [ngIf]="experiment.CheckableItem == true">{{experiment.code.toUpperCase()}}</td>
<td>mean</td>
</tr>
</tbody>
</table>
If some of the properties will not be available in the interface then they should be defined optional. Notice the question mark.
interface TimeData {
time?: string;
data?: {SD?: string, SEM?: string, N?: string, MEAN?: string};
}
In the template, you could use *ngIf directive with an else clause to show '--' if some properties are not available. Try the following
Controller
import { Component, VERSION, ElementRef } from '#angular/core';
import { Router } from '#angular/router';
interface TimeData {
time?: string;
data?: {SD?: string, SEM?: string, N?: string, MEAN?: string};
}
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
timeData: TimeData[] = [
{time: '15', data: {SD: '10', SEM: '20', N: '30', MEAN: '75'}},
{data: {SD: '10', SEM: '20', N: '30', MEAN: '75'}},
{time: '15', data: {SD: '10', SEM: '20', N: '30'}},
{data: {SEM: '20', N: '30', MEAN: '75'}},
];
constructor(private _router: Router, private _el: ElementRef) { }
}
Template
<table>
<tr>
<th>Time</th>
<th>SD</th>
<th>SEM</th>
<th>N</th>
<th>MEAN</th>
</tr>
<tr *ngFor="let data of timeData">
<ng-container *ngIf="data.time; else emptyData">
<td>{{ data.time }}</td>
</ng-container>
<ng-container *ngIf="data.data.SD; else emptyData">
<td>{{ data.data.SD }}</td>
</ng-container>
<ng-container *ngIf="data.data.SEM; else emptyData">
<td>{{ data.data.SEM }}</td>
</ng-container>
<ng-container *ngIf="data.data.N; else emptyData">
<td>{{ data.data.N }}</td>
</ng-container>
<ng-container *ngIf="data.data.MEAN; else emptyData">
<td>{{ data.data.MEAN }}</td>
</ng-container>
<ng-template #emptyData>
<td>--</td>
</ng-template>
</tr>
</table>
Working example: Stackblitz
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>