I have a registration form where an user could add one/more Address(es), one/more phone(s) to a new or existing Organization, Address and Phone are actually (reusable) components.
For example, Phone component is similar with that one from Android contact details, it has 3 FormControls: a Type (drop-down list), number (input) and a remove button.
At the end, the entire form must be submitted to save the information.
Question: how can I dynamically add Phones to the form and display already existing ones? (code & markup below). I'd much appreciate your time and effort answering me!
organization-detail.component.ts
import { Component, Input } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormControl, Validators } from '@angular/forms';
import { Organization } from './organization';
import { PhoneDetailComponent } from "../phone-detail/phone-detail.component";
@Component({
selector: 'organization-detail',
templateUrl: './organization-detail.component.html',
styleUrls: ['./organization-detail.component.css']
})
export class OrganizationDetailComponent {
organizationForm: FormGroup;
constructor (private fb: FormBuilder){
this.createForm();
}
createForm() {
this.organizationForm = this.fb.group({
accountingId: [''],
externalId: '',
isHost: false,
logoPath: '',
name: ['',
[Validators.required,
Validators.minLength(4),
Validators.maxLength(24)]
], // <--- the FormControl called "name"
notes: '',
registrationNo: '',
vATId: '',
webSite: '',
phones: new FormArray([])
//phones: new Array<PhoneDetailComponent>
});
}
onSubmit() {
console.log(this.organizationForm);
}
onAddPhone() {
const control = new PhoneDetailComponent();
(<FormArray>this.organizationForm.get('phones')).push(control);
}
}
organization-detail.component.html
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
<h2>Organization Detail</h2>
<form [formGroup]="organizationForm" (ngSubmit)="onSubmit()" class="form-horizontal">
<div class="form-group">
<label for="isHost"> Is host:</label>
<input type="checkbox" id="isHost" formControlName="isHost" class="check" />
</div>
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" class="form-control" formControlName="name" placeholder="Organization name" />
<span
*ngIf="!organizationForm.get('name').valid && organizationForm.get('name').touched"
class="help-block">Please enter a valid name!</span>
</div>
<div class="form-group">
<label for="accountingId">Accounting Id:</label>
<input type="text" id="accountingId" class="form-control" formControlName="accountingId" />
</div>
<div class="form-group">
<label for="externalId">External Id:</label>
<input type="text" id="externalId" class="form-control" formControlName="externalId" />
</div>
<div class="form-group">
<label for="registrationNo">Registration No:</label>
<input type="text" id="registrationNo" class="form-control" formControlName="registrationNo" />
</div>
<div class="form-group">
<label for="vATId">VAT Id:</label>
<input type="text" id="vATId" class="form-control" formControlName="vATId" />
</div>
<div class="form-group">
<label for="webSite">Web site:</label>
<input type="text" id="webSite" class="form-control" formControlName="webSite" />
</div>
<div class="form-group">
<label for="logoPath">Logo path:</label>
<input type="text" id="logoPath" class="form-control" formControlName="logoPath" />
</div>
<div class="form-group">
<label for="notes">Notes:</label>
<textarea id="notes" class="form-control" formControlName="notes" rows="3"></textarea>
</div>
<div formArrayName="phones">
<h4>Phones:</h4>
<button class="btn btn-default" type="button"
(click)="onAddPhone()">
Add phone
</button>
<div class="form-group"
*ngFor="let phoneControl of organizationForm.get('phones').controls; let i = index">
<!--<input type="text" class="form-control" [formControlName]="i">-->
<phone-detail></phone-detail>
</div>
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control">
<span class="input-group-btn">
<button class="btn btn-default" type="button">Go!</button>
</span>
</div>
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>
</div>
</div>
</div>
<p>Form value: {{ organizationForm.value | json }}</p>
<p>Form status: {{ organizationForm.status | json }}</p>
phone-detail.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { Phone } from "./phone";
@Component({
selector: 'phone-detail',
templateUrl: './phone-detail.component.html',
styleUrls: ['./phone-detail.component.css']
})
export class PhoneDetailComponent implements OnInit {
@Input('phoneItem') item: Phone;
constructor() { }
ngOnInit() {
}
}
phone-detail.component.html
<div class="container">
<div class="row">
<div class="col-xs-3">
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="col-xs-7">
<input type="text" class="form-control">
</div>
<div class="col-xs-2">
<button class="btn">X</button>
</div>
</div>
</div>
In your parent, have the formarray:
phones: this.fb.array([])
Then in parent template pass each formgroup inside this formarray to the child:
<button (click)="addPhone()">Add phone</button>
<div formArrayName="phones">
<div *ngFor="let ctrl of organizationForm.controls.phones.controls; let i=index">
<button (click)="removePhone(i)">X</button>
<phone-detail [group]="ctrl"></phone-detail>
</div>
</div>
in child use @Input
:
@Input() group: FormGroup;
and in the template add the form controls and formgroup:
<div [formGroup]="group">
<select formControlName="type">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
<input type="text" formControlName="num">
</div>
And finally the removing and adding of form groups which are placed in parent:
initPhone() {
return this.fb.group({
type: [''],
num: ['']
});
}
addPhone() {
const control = this.organizationForm.controls.phones;
control.push(this.initPhone());
}
removePhone(i: number) {
const control = this.organizationForm.controls.phones;
control.removeAt(i);
}
This article is worth reading regarding this question!
Thanks a lot for your responses, helped a lot! I used an event to trigger removing a phone registration. Solution suitable for my situation is:
In parent organization-edit.component.html:
<div formArrayName="phones">
<h4>Phones:</h4>
<button class="btn btn-default" type="button"
(click)="onAddPhone()">
Add phone
</button>
<div class="form-group"
*ngFor="let phoneControl of organizationForm.get('phones').controls; let i = index">
<phone-detail [formGroupName]="i" (phoneDeleted)="onDeletePhone(i)"></phone-detail>
</div>
</div>
organization-edit.component.ts (former detail!):
createForm() {
this.organizationForm = this.fb.group({
accountingId: [''],
......
phones: new FormArray([])
});
onAddPhone() {
const control = new FormControl(null, Validators.required);
(<FormArray>this.organizationForm.get('phones')).push(control);
}
onDeletePhone(index: any){
(<FormArray>this.organizationForm.get('phones')).removeAt(index);
}
phone-detail.component.html
<div class="row">
<div class="col-xs-3">
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
<div class="col-xs-7">
<input type="number" class="form-control">
</div>
<div class="col-xs-2">
<button
type="button"
class="btn btn-danger"
(click)="onDeletePhone()">X</button>
</div>
phone-detail.component.ts:
export class PhoneDetailComponent implements OnInit {
phoneForm: FormGroup;
@Input('phoneItem') item: Phone;
@Output() phoneDeleted = new EventEmitter<void>();
@Output() phone: Phone;
constructor(private fb: FormBuilder) {
this.phoneForm = this.fb.group({
type: [''],
number: ['']
});
}
onDeletePhone(){
this.phoneDeleted.emit();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With