Reputation: 8681
I am creating an reactive form in angular 7 with the ability to dynamically add and delete rows in a table.
What I need is
I need to add the values entered in these controls to the table after the user clicks the Add button.
The table rows should contain these values along with the delete button in the last column.
How do I achieve this using reactive forms.
Currently my UI looks like this
<form [formGroup]="frmFirm" (ngSubmit)="onSubmit()">
<div *ngIf="FirmDetails && FirmDetails.Firm" class="card-body scrollClass" style="width:100%">
<div class="form-group row">
<label for="inputEmail" class="col-md-1 col-form-label header">Websites</label>
<div class="col-md-3">
<div *ngIf="!EditMode">{{FirmDetails.Websites}}</div>
</div>
</div>
</div>
</form>
Component
import { Component, Injectable, NgZone, ViewEncapsulation, ViewChild, Input } from '@angular/core';
import { OnInit } from '@angular/core';
import { FirmService } from '../services/firm.service';
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import { CommonDataService} from '../services/common.data.service';
import {FormGroup, FormControl, FormBuilder} from '@angular/forms';
@Component({
selector: 'mgr-firm',
templateUrl: './firm.component.html'
})
export class FirmComponent implements OnInit {
private Error: string;
public FirmDetails: any;
public EditMode: boolean;
public Editor = ClassicEditor;
public EditorConfig : string;
public events: string[] = [];
@Input() FirmId: number;
DateFoundedDate: Date;
public frmFirm: FormGroup;
constructor( private _fb: FormBuilder, private firmService: FirmService, private commonDataService: CommonDataService) {
}
ngOnInit() {
this.getFirmDetails();
this.initializeFormModel();
}
initializeFormModel() {
this.frmFirm = this._fb.group({
firmName: [''],
shortName: [''],
alternateName: [''],
dateFounded: [''],
firmHistory: [''],
People: [''],
});
}
setFormValues(FirmDetails: any) {
this.frmFirm.patchValue({
firmName: FirmDetails.Firm.NAME,
shortName: FirmDetails.Firm.SHORT_NAME,
alternateName: FirmDetails.Firm.ALTERNATE_NAME,
dateFounded: FirmDetails.Firm.DATE_FOUNDED,
firmHistory: FirmDetails.Firm.HISTORY_HTML,
People: FirmDetails.People
});
}
getFirmDetails() {
if (this.FirmId != null) {
this.firmService.getFirmDetails(this.FirmId)
.subscribe(data => {
this.FirmDetails = data;
this.setFormValues(this.FirmDetails);
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
}
get dateFoundedDate(): Date {
var dateString = this.FirmDetails.Firm.DATE_FOUNDED;
var seconds = parseInt(dateString.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
var date = new Date(seconds);
return date;
}
saveManager() {
this.firmService.createFirm(this.FirmDetails)
.subscribe(data => {
this.getFirmDetails();
this.EditMode = !this.EditMode;
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
dateFoundedChanged(dateFoundedDate: Date) {
this.DateFoundedDate = dateFoundedDate;
}
}
updated UI
<div class="form-group row">
<label for="inputEmail" class="col-md-1 col-form-label header">Websites</label>
<div class="col-md-3">
<!-- <div *ngIf="!EditMode">{{FirmDetails.Websites[0].WEBSITE_URL}}</div> -->
<!-- <input *ngIf="EditMode" kendoTextBox [readonly]="false" class="form-control"
/> -->
<div formArrayName="websites"
*ngFor="let item of frmFirm.get('websites').controls; let i = index; let last = last">
<div [formGroupName]="i">
<input formControlName="websiteUrl" placeholder="Website Url">
<input formControlName="username" placeholder="User Name">
<input formControlName="password" placeholder="Password">
<button (click)="removeWebsite()">Remove Website</button>
<button (click)="addWebsite()" *ngIf="last">Add Website</button>
</div>
</div>
</div>
</div>
updated component
import { Component, Injectable, NgZone, ViewEncapsulation, ViewChild, Input } from '@angular/core';
import { OnInit } from '@angular/core';
import { FirmService } from '../services/firm.service';
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import { CommonDataService} from '../services/common.data.service';
import {FormGroup, FormControl, FormBuilder, FormArray} from '@angular/forms';
@Component({
selector: 'mgr-firm',
templateUrl: './firm.component.html'
})
export class FirmComponent implements OnInit {
private Error: string;
public FirmDetails: any;
public EditMode: boolean;
public Editor = ClassicEditor;
public EditorConfig : string;
public events: string[] = [];
@Input() FirmId: number;
DateFoundedDate: Date;
public frmFirm: FormGroup;
constructor( private _fb: FormBuilder, private firmService: FirmService, private commonDataService: CommonDataService) {
}
ngOnInit() {
this.getFirmDetails();
this.initializeFormModel();
}
initializeFormModel() {
this.frmFirm = this._fb.group({
firmName: [''],
shortName: [''],
alternateName: [''],
dateFounded: [''],
firmHistory: [''],
People: [''],
websites: this._fb.array([
this.createWebsite()
])
});
}
// public addWebsite(): void {
// const websites = this.frmFirm.get('websites') as FormArray;
// websites.push(this._fb.control(''));
// }
public addWebsite(): void {
this.websites.push(this.createWebsite());
}
public removeWebsite(index: number): void {
const websites = this.frmFirm.get('websites') as FormArray;
websites.removeAt(index);
}
private createWebsite(): FormGroup {
return this._fb.group({
websiteUrl: [''],
username: [''],
password: ['']
});
}
get websites(): FormArray {
return <FormArray>this.frmFirm.get('websites');
}
setFormValues(FirmDetails: any) {
this.frmFirm.patchValue({
firmName: FirmDetails.Firm.NAME,
shortName: FirmDetails.Firm.SHORT_NAME,
alternateName: FirmDetails.Firm.ALTERNATE_NAME,
dateFounded: FirmDetails.Firm.DATE_FOUNDED,
firmHistory: FirmDetails.Firm.HISTORY_HTML,
People: FirmDetails.People
});
const websiteGroup = this._fb.group({
websiteUrl: FirmDetails.Websites[0].WEBSITE_URL,
username: FirmDetails.Websites[0].USERNAME,
password: FirmDetails.Websites[0].PASSWORD
});
this.frmFirm.setControl('websites', this._fb.array([websiteGroup]));
}
getFirmDetails() {
if (this.FirmId != null) {
this.firmService.getFirmDetails(this.FirmId)
.subscribe(data => {
this.FirmDetails = data;
this.setFormValues(this.FirmDetails);
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
}
get dateFoundedDate(): Date {
var dateString = this.FirmDetails.Firm.DATE_FOUNDED;
var seconds = parseInt(dateString.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
var date = new Date(seconds);
return date;
}
saveManager() {
this.firmService.createFirm(this.FirmDetails)
.subscribe(data => {
this.getFirmDetails();
this.EditMode = !this.EditMode;
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
dateFoundedChanged(dateFoundedDate: Date) {
this.DateFoundedDate = dateFoundedDate;
}
}
Upvotes: 2
Views: 6548
Reputation: 60518
I have something similar, but I add them as "blocks" instead of "rows". Click the "Add Another Address" button to add another block.
I'm sure the formatting is just a matter of a few modifications to the layout.
The more challenging part is the code. To have repeatable sets of fields, you need to use a FormArray where each element is its own FormGroup.
I have a complete example here: https://github.com/DeborahK/Angular-ReactiveForms/tree/master/Demo-Final
Well, the prior answer beat me to it ... but I'm leaving this as well as the link provides a complete working example.
Upvotes: 2
Reputation: 316
What you are looking for is FormArray
.
Angular documentation has it nicely explained with examples that cover your use case of needing to add and remove form group (in your case that would be row with firm details).
https://angular.io/guide/reactive-forms#dynamic-controls-using-form-arrays
Example of what should work in your case.
Template
<div formArrayName="companies"
*ngFor="let item of form.get('companies').controls; let i = index; let last = last">
<div [formGroupName]="i">
<input formControlName="firmName" placeholder="Firm name">
<input formControlName="shortName" placeholder="Short name">
<input formControlName="alternateName" placeholder="Alternate name">
<input formControlName="dateFounded" placeholder="Founded">
<input formControlName="firmHistory" placeholder="History">
<input formControlName="People" placeholder="People">
<button (click)="removeCompany()">Remove company</button>
<button (click)="addCompany()" *ngIf="last">Add company</button>
</div>
</div>
Component
...
public form: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit(): void {
this.form = this.formBuilder.group({
companies: this.formBuilder.array([ this.createCompany() ])
});
}
public addCompany(): void {
const companies = this.form.get('companies') as FormArray;
companies.push(this.createItem());
}
public removeCompany(index: number): void {
const companies = this.form.get('companies') as FormArray;
companies.removeAt(index);
}
private createCompany(): FormGroup {
return this.formBuilder.group({
firmName: [''],
shortName: [''],
alternateName: [''],
dateFounded: [''],
firmHistory: [''],
People: [''],
});
}
...
Upvotes: 3