I'm building an Angular application with PrimeNG. I have a form that contains a p-table (list of products) with inputs (quantity and price), when I select the first product from a dialog and enter the inputs (quantity and price), then select a second product, I lose the first quantity and price that enter for the first product.
This issue only happened when putting the table with inputs inside of the form.
Is there a right way to resolve this? My code is below.
HTML:
<form #form="ngForm" (ngSubmit)="valider(form)">
<p-table [value]="object.produits" >
<ng-template pTemplate="header">
<tr>
<th>Code Produit</th>
<th>Quantité</th>
<th>Price</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-produit>
<tr [pSelectableRow]="produit">
<td>{{produit.codeProduit}}</td>
<td>
<input type="text" size="10" pInputText name="quantite" [readonly]="action=='show'" pKeyFilter="num" [ngModel]="produit.quantite" (ngModelChange)="produit.quantite = $event;">
</td>
<td>
<input type="text" size="10" pInputText name="prixUnitaire" [readonly]="action=='show'" pKeyFilter="num" [ngModel]="produit.prixUnitaire"(ngModelChange)="produit.prixUnitaire = $event;">
</td>
</tr>
</ng-template>
<ng-template pTemplate="summary">
<button type="button" (click)="showDialogProduit()" pButton label="Ajouter Produit"></button>
</ng-template>
</p-table>
</form>
<p-dialog header="Liste des produits" [(visible)]="dialogProduit" >
<p-table #dt_produits [value]="produits" selectionMode="multiple" [(selection)]="operationCommodities.produits" >
<ng-template pTemplate="header">
<tr>
<th>Code</th>
<th>Désignation</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-produit>
<tr [pSelectableRow]="produit">
<td>{{produit.codeProduit}}</td>
<td>{{produit.designation}}</td>
</tr>
</ng-template>
<ng-template pTemplate="summary">
<button pButton type="button" label="Terminer" (click)="closeDialogProduit()"></button>
</ng-template>
</p-table>
</p-dialog>
TS:
showDialogProduit() {
this.dialogProduit = true;
}
closeDialogProduit() {
this.dialogProduit = false;
}
Stackblitz Demo:
https://stackblitz.com/edit/primeng-tablebasic-demo-a7bdgm?file=src%2Fapp%2Fapp.component.html
The problem is that input fields don't have a unique name which messes up how ngModel directive works. When I updated the code as follows, it was fixed. Note that the [name] attributes of both input fields are dynamic values based on produit.codeProduit
<tr [pSelectableRow]="produit">
<td>{{produit.codeProduit}} </td>
<td>
<input type="text" size="10" pInputText [name]="'quantite_' + produit.codeProduit" [readonly]="action=='show'" pKeyFilter="num" [ngModel]="produit.quantite" (ngModelChange)="produit.quantite = $event;">
</td>
<td>
<input type="text" size="10" pInputText [name]="'prixUnitaire_' + produit.codeProduit" [readonly]="action=='show'" pKeyFilter="num" [ngModel]="produit.prixUnitaire"(ngModelChange)="produit.prixUnitaire = $event;">
</td>
</tr>
Please check Forked stackblitz with updated code.
As an aside, I recommend you change your implementation to use Angular Reactive Forms instead of ngModel;
Related
I'm initializing my FormArray with this code
this.contents.forEach(content=> {
this.formArray.push( new FormControl(content.text, Validators.required));
});
and I want to link one FormControl with my textarea and change it with my ID.
<textarea matInput id="content-textarea" placeholder="Required" [formControlName]="formArray.at(id)"></textarea>
But I get this error message:
ERROR Error: Cannot find control with unspecified name attribute
How can i use a specific FormControl from a FormArray in my HTML Document?
EDIT: I cant use a FormControl if i dont have one. I got this error message because my Content-Array was empty and i tried to use the FormControl.
So i have a different problem. But thanks for your answers :)
What if you directly use formcontrol instead of formcontrolname
<textarea matInput id="content-textarea" placeholder="Required" [formControl]="formArray.controls[0]"></textarea>
demo
I did something like this, can't show demo it's part of the big module:
<tr *ngFor="let file of docInfoForm.controls.Documents.controls; let i = index;" [formGroup]="file" class="form-group">
<td>
<fa class="cust-file-icon" [name]="file.get('Name').value | fileTypeIcon"></fa>
<input [attr.id]="'file-type-' + i" type="hidden" class="form-control" formControlName="Type" placeholder="File Size">
</td>
<td class="left">
<text-control [ctr-id]="'file-name-' + i" [label-text]="'File Name'" formControlName="Name"
[error-text]="'File Name is required'" [mandatory]="true" [form-ctr]="file.controls.Name">
</text-control>
</td>
<td class="left">
<text-control [ctr-id]="'file-desc-' + i" [label-text]="'Description'" formControlName="Description"></text-control>
</td>
<td class="left">
<label for="{{'file-size-' + i}}" class="form-label">
<span class="file-size-label">Size</span>
<span class="file-size-value">{{ file.get('Size').value | fileSize: true }}</span>
<input [attr.id]="'file-size-' + i" type="hidden" class="form-control" formControlName="Size" placeholder="File Size">
</label>
</td>
<td>
<button class="btn btn-outline only-icon btn-round" (click)="removeFile(i)">
<clr-icon shape="times"></clr-icon>
</button>
</td>
</tr>
I'm trying to add a new row to the current table so that I can have a row under the list of data that can have a save button and an insert to be done e.g. save phone number. I have added html comments of where the new row will go but not sure how to do it in PrimeNg.
See the code below:
<div class="m_datatable m-datatable m-datatable--default m-datatable--loaded">
<p-dataTable [value]="personPhone.phones"
emptyMessage="{{l('NoData')}}"
paginator="false"
rows="5"
tableStyleClass="m-datatable__table">
<p-column header="{{l('Actions')}}" [style]="{'width':'130px','text-align':'center'}">
<ng-template let-record="rowData" pTemplate="body">
<button (click)="deletePhone(phone, personPhone)" class="btn btn-outline-danger m-btn m-btn--icon m-btn--icon-only m-btn--pill">
<i class="fa fa-times"></i>
</button>
</ng-template>
</p-column>
<p-column header="{{l('PhoneType')}}">
<ng-template let-record="rowData" pTemplate="body">
{{getPhoneTypeAsString(record.type)}}
</ng-template>
</p-column>
<p-column header="{{l('PhoneNumber')}}">
<ng-template let-record="rowData" pTemplate="body">
{{record.number}}
</ng-template>
</p-column>
<!--New row to go here-->
</p-dataTable>
</div>
I Just want a row like this:
<tr>
<td>
<button (click)="savePhone()" class="btn btn-sm btn-success">
<i class="fa fa-floppy-o"></i>
</button>
</td>
<td>
<select name="Type" [(ngModel)]="newPhone.type" class="form-control">
<option value="0">{{l("Mobile")}}</option>
<option value="1">{{l("Home")}}</option>
<option value="2">{{l("Business")}}</option>
</select>
</td>
<td><input type="text" name="number" [(ngModel)]="newPhone.number" class="form-control" /></td>
</tr>
First find the length of the data what you are getting like below?
public dataLength:number;
this.myService.getAllResult('query)
.subscribe((response: any[]) => {
this.data = response
this.dataLength = this.data.length;
}
Now in p-template body take another row and use *ngIf and check the dataLength is greater than or equal to current Index:
<ng-template pTemplate="body" let-i="rowIndex" let-data>
<tr>
<td>{{i + 1}}</td>
<td>{{data.name}}</td>
<td>{{data.email}}</td>
</tr>
<tr *ngIf="i >= (dataLength -1)">Print your data</tr>
</ng-template>
I think there should be something like $last and $first index in primeNg as we have in angular for *ngFor.
If we find the last index in Prime Ng. Then no need to take the another dataLength. But Above solution should work for you.
Using the incell editing for the primeng TurboTable, I am trying to execute a method by clicking on enter key. Here is the following code:
<td pEditableColumn>
<p-cellEditor>
<ng-template pTemplate="input">
<input type="text" [(ngModel)]="rowData.vin" (keyup.enter)="onKeyPress($event)">
</ng-template>
<ng-template pTemplate="output">
{{rowData.vin}}
</ng-template>
</p-cellEditor>
</td>
The problem is that the method onKeyPress is not executed by clicking on the enterKey. So I've tried with keypress as following:
<td pEditableColumn>
<p-cellEditor>
<ng-template pTemplate="input">
<input type="text" [(ngModel)]="rowData.vin" (keypress)="onKeyPress($event)">
</ng-template>
<ng-template pTemplate="output">
{{rowData.vin}}
</ng-template>
</p-cellEditor>
</td>
And what I've noticed is that the method onKeyPress is executed by clicking on any button except the enter Key.
I think onEditComplete event catches the enter key press.
Check https://www.primefaces.org/primeng/#/table under Events.
To call your function on enter key press you can do this:
<p-table ... (onEditComplete)="onEditComplete($event)">
...
<td [pEditableColumn]="rowData" pEditableColumnField="'vin'">
In your component:
onEditComplete(event) {
console.log(event.data);
}
I Tried to use keydown instead of Keyup and it works fine
<td pEditableColumn>
<p-cellEditor>
<ng-template pTemplate="input">
<input type="text" [(ngModel)]="rowData.vin"
(keydown.enter)="onKeyDown($event)">
</ng-template>
<ng-template pTemplate="output">
{{rowData.vin}}
</ng-template>
</p-cellEditor>
</td>
I have a quick question regarding Angular 4 and splicing.
Below is my Typescript code:
delete(appIndex: number): void {
this.apps.splice(appIndex, 1);
}
Below is my HTML Code:
<tr *ngFor="let app of apps; let i = index">
<td><input type="text" [(contenteditableModel)]="text1" tabindex="1"> {{ text1 }}</td>
<td><input type="text" [(contenteditableModel)]="text2" tabindex="2">{{ text2 }}</td>
<td><input type="text" [(contenteditableModel)]="text2" tabindex="3">{{ text3 }}</td>
<td class="actions">
<input type="button" value="Delete" (click)="delete(i)">
</td>
</tr>
The issue I am running into is 'Cannot read property 'splice' of undefined. When I further researched into this, I found that in Angular 1, '$scope' was used in order to access within the DOM. How do I replicate this in Angular 4?
UPDATE:
I have edited my code to read like:
Typescript:
delete(appIndex: number) {
this.apps.splice(appIndex, 1);
this.changeDetectorRef.detectChanges();
}
HTML:
<tr class="odd">
<td class="status"><img src="../../../assets/images/Red_Circle_1.png" class="redcircle"></td>
<td><input type="text" [(contenteditableModel)]="text4"> {{ text4 }}</td>
<td><input type="text" [(contenteditableModel)]="text5">{{ text5 }}</td>
<td><input type="text" [(contenteditableModel)]="text6">{{ text6 }}</td>
<td class="actions">
<input type="button" value="Delete" *ngFor="let app of apps; let i = index" (click)="delete(i)">
</td>
</tr>
Here is the plunker: http://plnkr.co/edit/2KaXodEe2CbQdLXZtVaC?p=preview
I'm not getting any errors now, but the delete button is not displaying at all.
Any suggestions would be appreciated.
Thank you.
Your code should work without problem so long as you are executing delete from the component whose template you called it from.
Here is basically the same code working: Live demo
I am new in Angular 4.x. I have a html table. Every row has a checkbox and a checkbox. I want to bind checkbox with button, so that when the checkbox is checked, the button is enabled, and when the checkbox in unchecked, the button is disabled:
here is a sample of code, but it is not working :
<tbody>
<tr *ngFor="let item of mf.data; let i = index;">
<td><input type="checkbox" value="" [checked]="item.checked"></td>
<td>{{i}}</td>
<td>{{item.name}}</td>
<td>{{item.email}}</td>
<td>{{item.age}}</td>
<td>{{item.city | uppercase}}</td>
<td><button type="button" class="btn btn-success" [disabled]="item.checked">start</button></td>
</tr>
</tbody>
Can you help me to make this work please ?
use [(ngModel)]. Because check will not enable the two-way binding. It just handles One-way changing
<td><input type="checkbox" value="" [(ngModel)]="item.checked"></td>
make the button disable not equal to item check, like this [disabled]="!item.checked"
<td><button type="button" class="btn btn-success" [disabled]="!item.checked">start</button></td>
Currently your checkbox only binds one way. To apply changes by clicking it, add the following to your input tag:
(change)="item.checked = !item.checked"
You don't need checked directive. Just binding the model is enough.
<tbody>
<tr *ngFor="let item of mf.data; let i = index;">
<td><input type="checkbox" [(ngModel)]="item.checked"></td>
<td>{{i}}</td>
<td>{{item.name}}</td>
<td>{{item.email}}</td>
<td>{{item.age}}</td>
<td>{{item.city | uppercase}}</td>
<td><button type="button" class="btn btn-success" [disabled]="!item.checked">start</button></td>
<pre>{{item.checked}}</pre>
</tr>
</tbody>
<tbody>
<tr *ngFor="let item of mf.data; let i = index;">
<td><input type="checkbox" value="" [checked]="item.checked"></td>
<td>{{i}}</td>
<td>{{item.name}}</td>
<td>{{item.email}}</td>
<td>{{item.age}}</td>
<td>{{item.city | uppercase}}</td>
<td><button type="button" class="btn btn-success" [disabled]="item.checked">start</button></td>
</tr>
</tbody>
Use this:
<td>
<div class="togglebutton">
<label>
<input type="checkbox" [checked]="record.status" (change)="changeStatus(record.id,$event)">
<span class="toggle"></span>
</label>
</div>
</td>