How to push new values at the click of a button in formArray without adding new fields. Fields already exist, which means I don't need new ones, I just need new values from those fields entered.
Look at my code:
<form [formGroup]="form" (submit)="submit()">
<div *ngFor="let data of contactFormGroup.controls; let i = index;" formArrayName="data">
<div [formGroupName]="i">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Pretraži prozivod...">
</mat-form-field>
<table mat-table [dataSource]="productSource" matSort class="mat-elevation-z8">
<!-- Position Column -->
<ng-container matColumnDef="id">
<th mat-header-cell mat-sort-header *matHeaderCellDef> Broj Proizvoda </th>
<td mat-cell *matCellDef="let element" > {{ element.id }} </td>
</ng-container>
<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell mat-sort-header *matHeaderCellDef> Naziv proizvoda </th>
<td mat-cell *matCellDef="let element"> {{ element.name }} </td>
</ng-container>
<!-- Weight Column -->
<ng-container matColumnDef="description">
<th mat-header-cell mat-sort-header *matHeaderCellDef> Opis proizvoda </th>
<td mat-cell *matCellDef="let element"> {{element.description}} </td>
</ng-container>
<!-- quantity Column -->
<ng-container matColumnDef="images" class="test" >
<th mat-header-cell mat-sort-header *matHeaderCellDef> quantity</th>
// i need this values but on click add new object in formArray
<td mat-cell *matCellDef="let element" class="test"> <input formControlName="quantity" type="text"> </td>
</ng-container>
<ng-container matColumnDef="images2">
<th mat-header-cell mat-sort-header *matHeaderCellDef> Add new </th>
<td mat-cell *matCellDef="let element"> <button mat-raised-button color="primary">Add new</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr (click)="test(row.id)" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSizeOptions]="[5,10,20]" showFirstLastButtons></mat-paginator>
<div class="button-submit">
<button (click)="openDialog2()" mat-raised-button color="warn">
Posalji narudzbu
</button>
</div>
</div>
</div>
</form>
Ts file:
ngOnInit() {
this.productSource.sort = this.sort;
this.productSource.paginator = this.paginator;
this.getProducts();
this.form = this.fb.group({
deliveryDate: [''],
data: this.fb.array([this.createContact()]),
note: [null]
});
this.contactList = this.form.get('data') as FormArray;
}
createContact(): FormGroup {
return this.fb.group({
id: ['', Validators.required],
quantity: ['', Validators.required]
});
}
addContact() {
this.contactList.push(this.createContact());
}
This code works well when I set fields and add function addContact(). When click show new fields and work good but in this situation, I don't want new fields. I have existing fields only to enter a quantity and push id and quantity in new FormArray.
This is an example what I need: https://ibb.co/1Tj2HNc
ON click button add new. If you have any question just ask
The current code works on principle when I add the first product:
console.log(my form.values)
data: Array(1)
0: {id: "", quantity: "99"}
And this is good only need id but ok... But when I add new quantity for new fields example 15 for another field and console.log my form values:
data: Array(1)
0: {id: "", quantity: "15 "}
override the old value and enter the new value.
It should add a new object in array with that value.
Example:
data: Array(2)
0: {id: "", quantity: "99"}
1: {id: "", quantity: "15"}
Also i need id :)
EDIT:
I forgot a piece of code:
get contactFormGroup() {
return this.form.get('data') as FormArray;
}
just create new function that accepts the index you want to insert the data into and the object you want to insert and it should do what you want.
assignContactForm(index: number, data: any): void {
// data is object of type {id: number, quantity: number }
this.contactFormGroup.controls[i].patchValue(data);
}
Related
I have a mat-toggle as a column in Mat-table.I am changing values on toggle on/off. However the issue is if I change the value then the value changes for all the rows. I want to do it only for the particular row. How can I do it.
HTML Code:
<ng-container matColumnDef="active">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Reschedule</th>
<td mat-cell *matCellDef="let element">
<mat-slide-toggle (change)="setMessage($event)">{{
message
}}</mat-slide-toggle>
</td>
</ng-container>
Typescript Code:
// Toggle Button Code
message = 'Disabled!';
setMessage(e) {
if (e.checked) {
this.message = 'Running';
} else {
this.message = 'Disabled!';
}
}
You need a property for every row to bind your toggle state to.
E.g.:
Component data:
....
const ELEMENT_DATA: PeriodicElement[] = [
{ position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H', activate: true, role: '1' },
...
Template:
...
<!-- toggle olumn -->
<ng-container matColumnDef="toggle">
<th mat-header-cell *matHeaderCellDef> toggle </th>
<td mat-cell *matCellDef="let element" >
<mat-slide-toggle [checked]="element.activate" (change)="updateActiveStatus(element)"></mat-slide-toggle>
</td>
</ng-container>
...
Check this Stackblitz for a working version.
I want child components in angular to separate Material Table(child) from other elements(parent), but the weird thing is that it seems I cannot get data from the parent or I get initialized data from parents and cannot refresh.
Here is the Code:
firstly, the parent .ts file:
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit{
ticketList: Ticket[] = [];
originalTicketList: Ticket[] = [];
filterAllForm: FormGroup;
labelList: string[];
homeLabelList: string[];
currentTicketId: string;
searchBy: string = '';
maxall : number = 100;
isLoading :boolean = true;
showRelatedTickets = true;
constructor(private ticket: TicketService,
private formBuilder: FormBuilder) { }
ngOnInit() {
this.filterAllForm = this.formBuilder.group({
startDate: [''],
endDate: ['']
});
this.getAllTicket();
}
get filterFormControls() { return this.filterAllForm.controls; }
getAllTicket() {
let queryParams = '';
console.log(this.currentTicketId) + " " + this.searchBy;
if (this.searchBy === 'id') {
queryParams = 'id=' + this.currentTicketId;
this.showRelatedTickets = true;
}else if(this.searchBy !== ''){
queryParams = this.searchBy + '=' + this.currentTicketId;
this.showRelatedTickets = false;
}
this.ticket.getAllTicket(queryParams).then((res: Ticket[]) => {
this.isLoading = false;
this.ticketList = res;
this.originalTicketList = this.ticketList;
});
}
searchTicket() {
this.isLoading = true;
this.getAllTicket();
}
}
the parent html like this:
<div class="ticket-container">
<mat-card *ngIf="isLoading" class="mat-card-style">
<mat-progress-spinner color="primary" mode="indeterminate">
</mat-progress-spinner>
</mat-card>
<div *ngIf="showRelatedTickets">
<app-dashboard-related-tickets-table [tablesource]="ticketList">
</app-dashboard-related-tickets-table>
</div>
<div *ngIf="!showRelatedTickets">
<app-dashboard-non-related-tickets-table [tablesource]="ticketList">
</app-dashboard-non-related-tickets-table>
</div>
</div>
and there are two children you can see, one is app-dashboard-related-tickets-table and another one is app-dashboard-non-related-tickets-table, they have a different format and changed by the button
showRelatedTickets. Here I will show the first one
the child(app-dashboard-related-tickets-table) .ts file:
#Component({
selector: 'app-dashboard-related-tickets-table',
templateUrl: './dashboard-related-tickets-table.component.html',
styleUrls: ['./dashboard-related-tickets-table.component.scss']
})
export class DashboardRelatedTicketsTableComponent implements OnInit, AfterViewInit{
dataSource: any;
displayedDetailColumns = ['id', 'caseStatus', 'anchorPoint', 'findingSourceSystem','caseCreateTimestamp', 'resolvedBy', 'resolution', 'resolutionNote'];
maxall: number = 100;
#Input() tablesource: Ticket[];
#ViewChild(MatPaginator, {static: false}) paginator: MatPaginator;
#ViewChild(MatSort, { static: true }) sort: MatSort;
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
constructor() { }
ngOnInit() {
console.log(this.tablesource);
this.dataSource = new MatTableDataSource(this.tablesource);
}
ngAfterViewInit(){
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.getPageSizeOptions();
}
getPageSizeOptions(): number[] {
if (this.dataSource.data.length>this.maxall){
return [20, 50, this.dataSource.data.length];
}else{
return [20, 50, this.maxall];
}
}
}
then the child(app-dashboard-related-tickets-table) .html file:
<table mat-table matTableExporter [dataSource]="dataSource" matSort class="mat-elevation-z8"
#exporter="matTableExporter">
<!-- Ticket Column -->
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="mat-column-id-source"> SIR ID </th>
<td mat-cell *matCellDef="let element" class="mat-column-id-source">
<a href='ticket/{{element.id}}'>{{element.id}}</a>
</td>
</ng-container>
<!-- Type Column event source -->
<ng-container matColumnDef="findingSourceSystem">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="mat-column-id-source"> Source </th>
<td mat-cell *matCellDef="let element" class="mat-column-id-source"> {{element.findingSourceSystem}} </td>
</ng-container>
<!-- Type Column Related Event count -->
<ng-container matColumnDef="anchorPoint">
<th mat-header-cell *matHeaderCellDef class="mat-column-related-ticket-num"> Related Cases </th>
<td mat-cell *matCellDef="let element" class="mat-column-related-ticket-num">
{{element.relatedVertices}} </td>
</ng-container>
<!-- Type Column event source -->
<ng-container matColumnDef="caseStatus">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="mat-column-case"> Case Status </th>
<td mat-cell *matCellDef="let element" class="mat-column-case"> {{element.caseStatus}} </td>
</ng-container>
<!-- Type Column event source -->
<ng-container matColumnDef="caseCreateTimestamp">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="mat-column-date"> Case Created </th>
<td mat-cell *matCellDef="let element" class="mat-column-date">
{{element.caseCreateTimestamp | date:'MMM d, y, h:mm:ss a'}}
</td>
</ng-container>
<ng-container matColumnDef="resolvedBy">
<th mat-header-cell *matHeaderCellDef class="mat-column-resolvedBy"> Resolved By </th>
<td mat-cell *matCellDef="let element" class="mat-column-resolvedBy"> {{element.resolvedBy}}
</td>
</ng-container>
<ng-container matColumnDef="resolution">
<th mat-header-cell *matHeaderCellDef class="mat-column-closeCode"> Close Code </th>
<td mat-cell *matCellDef="let element" class="mat-column-closeCode"> {{element.resolution}}
</td>
</ng-container>
<ng-container matColumnDef="resolutionNote">
<th mat-header-cell *matHeaderCellDef class="mat-column-closeNote"> Close Note </th>
<td mat-cell *matCellDef="let element" class="mat-column-closeNote"> {{element.resolutionNote}}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator [pageSizeOptions]="getPageSizeOptions()" showFirstLastButtons></mat-paginator>
<button mat-raised-button (click)="exporter.exportTable('csv')" class="export-button">Export Results</button>
So in the child component, I use #input tablesource, and display dataSource. but the result is like this:
The child component seems loaded because the export result button is already there, but the table didn't show. I just to do
console.log(tablesource)
I got []
This is the browser console log:
Any Idea what happened?
I was trying to reproduce here: https://stackblitz.com/edit/angular-hbzpvr
but I am sorry, I am not good at angular, so I am not sure how to load parent to page.
According to your console output, you use tablesource in your child component before it has been updated by the parent component. Instead of creating datasource in ngOnInit of the child component, define the tablesource property as a setter and create datasource in that setter:
#Input() set tablesource(value: Ticket[]) {
console.log(value);
this.dataSource = new MatTableDataSource(value);
}
See this stackblitz for a demo.
i have data in in a td similar to
Sugar:5;Onion:3;Carrot:9;Bread:9;
where i want to display like,
Sugar:5
Onion:3
Carrot:9
Bread:9
Inside a td in a table.
im trying to implement using angular material table
i have reached only this much, want to achieve like the above image for Items column
code:
html file
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="orderId">
<th mat-header-cell *matHeaderCellDef mat-sort-header> orderId </th>
<td mat-cell *matCellDef="let element"> {{element.orderId}} </td>
</ng-container>
<ng-container matColumnDef="customerId">
<th mat-header-cell *matHeaderCellDef mat-sort-header> customerId </th>
<td mat-cell *matCellDef="let element"> {{element.customerId}} </td>
</ng-container>
<ng-container matColumnDef="deliveryPincode">
<th mat-header-cell *matHeaderCellDef mat-sort-header> deliveryPincode </th>
<td mat-cell *matCellDef="let element"> {{element.deliveryPincode}} </td>
</ng-container>
<ng-container matColumnDef="orderDate">
<th mat-header-cell *matHeaderCellDef mat-sort-header> orderDate </th>
<td mat-cell *matCellDef="let element"> {{element.orderDate}} </td>
</ng-container>
<ng-container matColumnDef="items">
<th mat-header-cell *matHeaderCellDef mat-sort-header> items </th>
<td mat-cell *matCellDef="let element"> {{element.items}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
Upload
typescriptfile
import {MatSort} from '#angular/material/sort';
import {MatTableDataSource} from '#angular/material/table';
import * as XLSX from 'xlsx';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'antstack-problem';
arrayBuffer:any;
file:File;
displayedColumns: string[] = ['orderId', 'customerId', 'deliveryPincode','orderDate', 'items' ];
dataSource = new MatTableDataSource();
#ViewChild(MatSort, {static: true}) sort: MatSort;
ngOnInit() {
this.dataSource.sort = this.sort;
}
incomingfile(event) {
this.file= event.target.files[0];
}
Upload() {
let fileReader = new FileReader();
fileReader.onload = (e) => {
this.arrayBuffer = fileReader.result;
var data = new Uint8Array(this.arrayBuffer);
var arr = new Array();
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
var workbook = XLSX.read(bstr, {type:"binary"});
var first_sheet_name = workbook.SheetNames[0];
var worksheet = workbook.Sheets[first_sheet_name];
this.dataSource = new MatTableDataSource(XLSX.utils.sheet_to_json(worksheet,{raw:true}));
this.dataSource.sort = this.sort;
console.log(XLSX.utils.sheet_to_json(worksheet,{raw:true}));
}
fileReader.readAsArrayBuffer(this.file);
}
}
I would go for a Map() and ngFor, which gives you some stuff like this:
<ul>
<li *ngFor="let recipient of map | keyvalue">
{{recipient.key}} --> {{recipient.value}}
</li>
</ul>
https://stackblitz.com/edit/angular-map-keyvalue
(seen here)
You can wrap the items in the p tag. This will also break this in the next line.
I have such issue I need to extract data from:
<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef> Title </th>
<td mat-cell *matCellDef="let row"><div id="{{'make_editable' + row.title}}">{{row.title}}</div></td>
</ng-container>
...
<ng-container matColumnDef="buttonEdit">
<th mat-header-cell *matHeaderCellDef> buttonEdit </th>
<td mat-cell *matCellDef="let row"><button (click)="makeEditable(row.title)" class="mat-raised-button" form="myform">Edit</button></td>
/ng-container>
makeEditable(title){
console.log(this.checkOutForm.value.title);
let id:string="make_editable"+title;
if(this.toggleEdit()){
document.getElementById(id).innerHTML = `<form id="myform" (ng-submit)="extractFormValues()" [formGroup]="checkOutForm"><input type="text" value="${title}"></form>`;
}
else {
document.getElementById(id).innerHTML = title;
console.log(this.checkOutForm.controls.title.value);
}
The button is outside the function. If edit button is clicked then input wraps data and make it active. I need to do it without refreshing all page.The button Edit is outside of the form
You could create on the type, that the rows have, a boolean property - isEdited.
makeEditable(title) would find the correct row and toggle its isEdited value. It would also set:
checkOutForm.setValue({title: title});
(if isEdited was set to true).
In the template you would wrap the whole block in
<form id="myform" (ng-submit)="extractFormValues()" [formGroup]="checkOutForm"</form>
and change the td for a title:
<td mat-cell *matCellDef="let row">
<div *ngIf="!row.isEdited">{{row.title}}</div>
<input *ngIf="row.isEdited" type="text" formControlName="title">
</td>
This only works if just one row is edited at once.
I've been trying for days to get data from input fields inside an Angular Material Table.
I am basically populating a table with values that come from an API, however whenever we don't get any date, in my case a course doesn't have a scheduled date set, i am inserting a text box where the value should be displayed so the user can set a date for that specific course.
Like this:
Note: Sorry for the censoring, work related names had to be removed.
This is my html code:
<mat-card>
<form #traineeForm="ngForm">
<mat-form-field>
<input readonly matInput type="text" name="name" [ngModel] = "trainee.name" #name="ngModel">
</mat-form-field>
<mat-form-field>
<input readonly matInput email type="text" name="email" [ngModel] = "trainee.email" #email="ngModel">
</mat-form-field>
<mat-form-field>
<input readonly matInput type="text" name="type" [ngModel] = "trainee.type" #type="ngModel">
</mat-form-field>
<button mat-raised-button color ="primary" type ="submit">Edit</button>
<button mat-raised-button color ="warn" type ="submit" (click)="onDelete(trainee.id)">Delete</button>
</form>
</mat-card>
<br>
<mat-card>
<table mat-table [dataSource]="courses" class="mat-elevation-z8">
<ng-container matColumnDef="courseOrderID">
<th mat-header-cell *matHeaderCellDef>Course Order ID</th>
<td mat-cell *matCellDef="let element"> {{element.courseOrderID}}</td>
</ng-container>
<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef>Course Title </th>
<td mat-cell *matCellDef="let element"> {{element.title}}</td>
</ng-container>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>Course Description </th>
<td mat-cell *matCellDef="let element"> {{element.description}}</td>
</ng-container>
<ng-container matColumnDef="duration">
<th mat-header-cell *matHeaderCellDef>Duration </th>
<td mat-cell *matCellDef="let element"> {{element.duration}}</td>
</ng-container>
<ng-container matColumnDef="scheduledDate">
<th mat-header-cell *matHeaderCellDef>Scheduled Date </th>
<td mat-cell *matCellDef="let element">
<mat-form-field>
<input matInput [matDatepicker]="picker" placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
{{element.scheduledDate}}</td>
</ng-container>
<ng-container matColumnDef="trainer">
<th mat-header-cell *matHeaderCellDef>Trainer </th>
<td mat-cell *matCellDef="let element">
<mat-form-field><input matInput color="warn" *ngIf="!element.trainer"></mat-form-field> {{element.trainer}}</td>
</ng-container>
<ng-container matColumnDef="save">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let element">
<button mat-raised-button color ="primary" type ="submit" (click)="onSaveAssignment(trainee, element, picker)">Save</button></td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="coursesdisplayColumns">
</tr>
<tr mat-row *matRowDef="let courses; columns: coursesdisplayColumns"></tr>
</table>
<br>
</mat-card>
And this is my TypeScript code:
import { Trainee } from '../trainees.model';
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs';
import { TraineesService } from '../../trainees.service';
import { ActivatedRoute, ParamMap } from '#angular/router';
import { Course } from '../../courses/courses.model';
import { CoursesService } from '../../courses.service';
import { Assignment } from '../../assignments/assignments.model';
import { NgForm } from '#angular/forms';
#Component({
selector: 'app-trainee-details',
templateUrl: './trainee-details.component.html',
styleUrls: ['./trainee-details.component.css']
})
export class TraineeDetailsComponent implements OnInit, OnDestroy {
private traineeId: string;
trainee: Trainee;
assignment: Assignment;
courses: Course[] = [];
coursesdisplayColumns = ['courseOrderID', 'title','description','duration','scheduledDate','trainer','save'];
constructor(public traineeService: TraineesService, public route: ActivatedRoute, public coursesService: CoursesService){}
ngOnInit() {
this.route.paramMap.subscribe((paramMap: ParamMap) => {
if(paramMap.has('traineeId')){
this.traineeId = paramMap.get('traineeId');
this.trainee = this.traineeService.getTrainee(this.traineeId);
}
});
this.coursesService.getCoursesByJob(this.trainee.job);
this.coursesService.getCoursesUpdateListener().subscribe((courses: Course[]) =>{
this.courses = courses;
});
}
onDelete(traineeId: string)
{
this.traineeService.deleteTrainee(traineeId);
}
onSaveAssignment(trainee: Trainee, selectedCourse: Course, dateForm: Date){
console.log(trainee.id);
console.log(selectedCourse.description);
console.log(dateForm);
}
ngOnDestroy() {
}
}
When i call onSaveAssignment(), the trainee ID and course ID are getting logged in the console correctly as those are defined in typescript, but I have no ideea how should i bring that date selected in the interface, i tried with ng-model but it did not work and I had to define a form for each input and still did not work.
Is there any way to get that values from inputs on each row when the Save button is pressed ?
Or if i put 1 button for all of them is there any way to do a foreach on every input value in the interface ?
You can get the values with ngModel by creating an object containing all values using the index as attribute.
In you component, put an object:
public myDates : any = {};
Then use ngModel with the index for your input date:
<ng-container matColumnDef="scheduledDate">
<th mat-header-cell *matHeaderCellDef>Scheduled Date </th>
<td mat-cell *matCellDef="let element; let i = index">
<mat-form-field>
<input matInput [(ngModel)]="myDates[i]" [matDatepicker]="picker" placeholder="Choose a date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</td>
</ng-container>
For each row, it will add an attribute to the object myDates. Using index permits to guarantee uniqueness. Your object will look like: {1: date1, 2: date2 ...}.
Then you can get the value by knowing the index of the row.
You can get it directly when clicking on the button:
<ng-container matColumnDef="save">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let element; let i = index">
<button mat-raised-button color ="primary" type ="submit" (click)="onSaveAssignment(trainee, element, myDates[i])">Save</button>
</td>
</ng-container>