How to create Angular 2 Custom Component with multiple input fields - javascript

I have created a simple custom component in Angular 2 by implementing the CustomValueAccessor interface and it works fine. This component has just 1 input field in it. e.g. Postcode component
<postcode label="Post Code" cssClass="form-control" formControlName="postcode"> </postcode>
Now, I want to expand on this example and create an address component that has multiple input fields, line1, line 2, line3, postcode and country.
I have expanded the postcode example to include multiple fields and I can see the input component coming up on screen. However, the address component values are not being reflected in the host form.
Appreciate any pointer in this direction.
Example :
import { Component, OnInit, Input } from '#angular/core';
import { FormControl, FormGroup, ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR, Validators } from '#angular/forms';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'address',
templateUrl: './address.component.html',
styleUrls: ['./address.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: CommonAddressComponent,
multi: true
}
]
})
export class CommonAddressComponent implements OnInit , ControlValueAccessor {
addressForm : FormGroup
ngOnInit() {
this.addressForm = this.formBuilder.group({
line_1: '',
line_2: '',
});
}
/*private addressForm = new FormControl()*/
private subscription: Subscription;
public disabled: any = false;
constructor(private formBuilder: FormBuilder) { }
//model to view
writeValue(value: any) {
console.log("value = " + value)
this.addressForm.setValue(value);
}
registerOnChange(fn: (value: any) => void) {
console.log("registerOnChange = " + fn)
this.addressForm.valueChanges.subscribe(fn);
}
registerOnTouched() {}
}
Template file :
<div class="form-group" [formGroup]="addressForm">
<input class="form-control" type="text" formControlName="line_1" />
<input class="form-control" type="text" formControlName="line_2" />
</div>
Host Form component file:
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup, Validators, FormBuilder} from '#angular/forms';
#Component({
selector: 'app-contacts',
templateUrl: './contacts.component.html',
styleUrls: ['./contacts.component.scss']
})
export class ContactsComponent implements OnInit {
contactsForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.contactsForm = this.fb.group({
name: 'test', // <--- the FormControl called "name"
postcode: 'tester111', // <--- the FormControl called "name"
line_3: '111', // <--- the FormControl called "name"*/
addressForm: new FormGroup({
line_1: new FormControl('I am line 1', Validators.minLength(2)),
line_2: new FormControl('I am line 2')
}),
});
}
ngOnInit() {
}
}
Host Form Component Template file:
<form [formGroup]="contactsForm">
<p>Form value: {{ contactsForm.value | json }}</p>
<p>Form status: {{ contactsForm.status | json }}</p>
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div>
<postcode label="Post Code" cssClass="form-control" formControlName="postcode"> </postcode>
</div>
<div class="form-group">
<address-line cssClass="form-control" name="line3" label="line 3" elementName="line3"
elementID="line3" formControlName="line_3"> </address-line>
</div>
<!--<div [formGroup]="contactsForm.addressForm"> -->
<div >
<address formGroupName="addressForm"> </address>
</div>
</form>

After multiple attempts, I was able to get a custom control with multiple input fields in Angular working. Code for the same goes as follows:
Custom component with multiple input fields
import { Component, OnInit, Input, ViewChild } from '#angular/core';
import { FormControl,NgForm, FormGroup, ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR, Validators, NgModel } from '#angular/forms';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'address',
templateUrl: './address.component.html',
styleUrls: ['./address.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: AddressComponent,
multi: true
}
]
})
export class AddressComponent implements OnInit , ControlValueAccessor {
addressForm : FormGroup
#Input() label: string;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.addressForm = this.formBuilder.group({
line1: '',
line2: '',
line3: '',
line4: '',
});
}
writeValue(value: any) {
if (value) {
this.addressForm.setValue(value);
}
}
registerOnChange(fn: (value: any) => void) {
console.log("registerOnChange = " + fn)
this.addressForm.valueChanges.subscribe(fn);
}
registerOnTouched() {}
}
Template of Custom Component
Template Code
<div class="form-group" [formGroup]="addressForm">
<input type="text" name="line1" class="form-control" type="text" formControlName="line1" />
<input type="text" name="line2" class="form-control" type="text" formControlName="line2" />
<input type="text" name="line3" class="form-control" type="text" formControlName="line3" />
<input type="text" name="line4" class="form-control" type="text" formControlName="line4" />
</div>
Host or Parent Component class
import { Component } from '#angular/core';
import { NgForm, Validators, FormControl, FormGroup } from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
pageForm: FormGroup = new FormGroup({
address: new FormGroup({
line1: new FormControl('',Validators.required),
line2: new FormControl('',Validators.required),
line3: new FormControl('',Validators.required),
line4: new FormControl('')
}),
})
}
4.
<div class="container">
<form [formGroup]="pageForm">
<p>Form value: {{ pageForm.value | json }}</p>
<p>Form status: {{ pageForm.status | json }}</p>
<address label="Line 1" formControlName="address" > </address>
</form>
</div>

Note that Host or Parent Component class must declare the "address" field as a FormControl, not a FormGroup:
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
pageForm: FormGroup = new FormGroup({
address: new FormControl({
line1: new FormControl('',Validators.required),
line2: new FormControl('',Validators.required),
line3: new FormControl('',Validators.required),
line4: new FormControl('')
}),
})
}

Related

angular form is not detecting the value changing

I don't know why the form is not working!
the input does not change the values in the form.
and the valueChanges is not detecting anything. any help?
applied on stackblitz:
https://stackblitz.com/edit/angular-ivy-w68v69?file=src/app/app.component.ts
import { Component, VERSION } from "#angular/core";
import { FormControl, FormGroup, Validators } from "#angular/forms";
#Component({
selector: "my-app",
template: `
<form [formGroup]="form">
<input
FormControlName="patientName"
placeholder="Enter Patient Name ..."
/>
</form>
`,
styleUrls: ["./app.component.css"]
})
export class AppComponent {
form: FormGroup;
ngOnInit() {
this.form = new FormGroup({
patientName: new FormControl("")
});
this.form.get("patientName").valueChanges.subscribe(x => console.log(x));
this.form.valueChanges.subscribe(next => {
console.log("valueChanges didtted");
console.log(next);
});
console.log("form created");
}
}
The property FormControlName in the HTML part should have the 'F' in lowercase : formControlName="patientName".
It you correct this, it works : https://stackblitz.com/edit/angular-ivy-cjntrr?file=src/app/app.component.ts

is it correct way to bind multiple values inside ngFor?

trying to understand how to bind input values in ngFor in below code both are printing same values , trying to set different value for each input , where i am making mistake ?
app.comp.html
<div *ngFor="let ques of sub.question">
<label for="subQuestionsInput">{{ques.questionText}}</label>
<input type="subQuestionsInput" class="form-control" *ngIf= "ques.answerType === 'TEXT_NUMBER'" name="TEXT_NUMBER" placeholder="Enter Dose amount Left" [(ngModel)]="TEXT_NUMBER">
<input type="subQuestionsInput" class="form-control" *ngIf= "ques.answerType === 'TEXT_DATE'" name="TEXT_DATE" placeholder="Enter Next Dose Date" [(ngModel)]="TEXT_DATE">
</div>
app.comp.ts
import {
Component,
OnInit
} from '#angular/core';
import {
ApiService
} from './api.service';
import {
EventService
} from './format-questions/format-questions.service'
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent Implement OnInit {
TEXT_NUMBER: string;
TEXT_DATE: string;
constructor(private dataService: ApiService, private eventService: EventService) {}
ngOnInit() {
console.log(TEXT_NUMBER);
console.log(TEXT_DATE);
}
}

Bind reactive forms with custom input etc

I create a custom input and other generic element
and at that point I want to bind it to reactive forms. Now I got null as value.
HTML
<label for="name">
<span>{{title}}</span>
<input type="text" name="name" formControlName="name">
</label>
Component
export class AppInputComponent implements OnInit {
#Input() name: string;
#Input() title?: string;
#Input() form: FormGroup;
#Input() options?: Array<string>;
constructor() { }
ngOnInit() {
}
}
This one have its own module file
#NgModule({
declarations: [
GFormsFields.AppTextboxComponent,
GFormsFields.AppSelectComponent,
GFormsFields.AppInputComponent,
GFormsFields.AppCheckboxComponent
],
imports: [
CommonModule,
BrowserModule,
],
exports: [
GFormsFields.AppTextboxComponent,
GFormsFields.AppSelectComponent,
GFormsFields.AppInputComponent,
GFormsFields.AppCheckboxComponent
],
providers: [],
bootstrap: []
})
And now I want to bind it to place where I create the reactive form.
HTML
<form [formGroup]="reactiveForms" (ngSubmit)="onSubmit()">
<app-app-input [title]="'First Name Dude'" [name]="'firstName'" [form]="'form'"></app-app-input>
<button type="submit" [disabled]="!reactiveForms.valid">Submit</button>
</form>
Component
import { Component } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms'
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
reactiveForms = new FormGroup({
name: new FormControl
});
onSubmit(): void {
console.log(this.reactiveForms);
}
}
How can I pass from this custom input to app (it's as test purpose. This will be nested at other component).
What did I do wrong?
change AppInputComponent.html like this :
<label for="name">
<span>{{title}}</span>
<input type="text" name="name" [formControl]="form.controls.name">
</label>
and use component like this :
<app-app-input [title]="'First Name Dude'" [name]="'firstName'" [form]="reactiveForms"></app-app-input>
===============================
with above changes your problem will be solved but i would suggest u extra changes for better design .
change AppInputComponent.ts like this :
export class AppInputComponent implements OnInit {
#Input() name: string;
#Input() title?: string;
#Input() nameControl: FormControl;
#Input() options?: Array<string>;
constructor() { }
ngOnInit() {
}
}
change AppInputComponent.html like this :
<label for="name">
<span>{{title}}</span>
<input type="text" name="name" [formControl]="nameControl">
</label>
and finally use it like this :
<app-app-input [title]="'First Name Dude'" [name]="'firstName'" [nameControl]="reactiveForms.controls.name"></app-app-input>
AppInputComponent only needs FormControl of "name" not whole FormGroup . so it is not a good design to pass the entire FormGroup .

How to create nested formgroup and take value

I have created a form but I am not able to create nested form as I need to add certain input fields dynamically which I am getting but now what is required is like when I click add a link it should add form nested.
Form image
Add button near input field should add only social media link and GitHub input field others will remain same.
HTML
<div class="container">
<p> </p>
<div>
<form [formGroup]="searchForm" novalidate (ngSubmit)="submit(searchForm.value)">
<div formArrayName="properties">
<div *ngFor="let prop of searchForm.get('properties').controls; let i = index">
<div class="row" [formGroupName]="i">
<select formControlName="desg" class="form-control">
<option value="CEO">CEO</option>
<option value="CTO">CTO</option>
<option value="CMO">CMO</option>
<option value="Project Manager">Project Manager</option>
</select>
<input formControlName="name" type="text" class="form-control" placeholder="Name">
<input formControlName="linkedin" type="text" class="form-control" placeholder="LinkedIn Url">
<input formControlName="github" type="text" class="form-control" placeholder="Github Url">
<button *ngIf="searchForm.controls['properties'].length > 1 " (click)="onDelProperty(i)">Delete</button>
</div>
</div>
</div>
<p>
</p>
<a (click)="onAddProperty()">Add</a>
<button class="btn btn-bold btn-primary" type="submit">submit</button>
</form>
</div>
</div>
component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, FormArray, FormControl } from '#angular/forms';
import { IcoService } from '../../services/ico.service';
import { debug } from 'util';
#Component({
selector: 'app-team',
templateUrl: './team.component.html',
styleUrls: ['./team.component.css']
})
export class TeamComponent implements OnInit {
searchForm: FormGroup;
searchForm1: FormGroup;
constructor(private fb: FormBuilder,private icoService: IcoService,) { }
ngOnInit() {
this.searchForm = this.fb.group({
properties: this.fb.array([this.createItem()])
});
}
createItem(): FormGroup {
return this.fb.group({
name: '',
desg: '',
social_media: '',
github:''
});
}
submit(formData: any) {
this.icoService.teamDetail(formData).subscribe((result) => {
console.log()
}, (err) => { console.log('err',err) })
}
onAddProperty() {
for(var i=1; i<=1; i++) {
(this.searchForm.get('properties') as FormArray).push(this.createItem());
}
}
onDelProperty(index:any) {
for(var i=1; i<=1; i++) {
(this.searchForm.get('properties') as FormArray).removeAt(index);
}
}
}
You Can try for following, This may help for you:
import { ToasterContainerComponent, ToasterService, ToasterConfig} from 'angular2-toaster';
// ======================== Internal Dependency Module Loading Start ================================
import {TabViewModule} from 'primeng/tabview';
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators,FormArray, FormControl} from '#angular/forms';
import { Router } from '#angular/router';
import { GlobalAjaxMethodHandler } from '../util/GlobalAjaxMethodHandler';
import { GlobalValidationService } from '../util/GlobalValidationService';
import { AjaxMethodName } from '../util/GlobalAjaxPathHandler';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
loginForm: FormGroup;
form = new FormGroup({
cities: new FormArray([
// new FormControl('drp')
this.initAddress()
]),
});
constructor(public _fb: FormBuilder, private _GlobalAjaxMethodHandler: GlobalAjaxMethodHandler, public router: Router,public _toasterService:ToasterService) { }
ngOnInit() {
this.formIntialization();
}
initAddress() {
// initialize our address
return this._fb.group({
third : ['', Validators.required],
second: [''],
first: [''],
four: ['']
});
}
//================================= Initialize form Value ============================================
formIntialization() {
this.loginForm = this._fb.group ({
Email : ['', [Validators.required, GlobalValidationService.emailValidator]],
Password : ['', [Validators.required, GlobalValidationService.passwordValidator]]
});
}
get cities(): FormArray { return this.form.get('cities') as FormArray; }
addCity() {
this.cities.push(this._fb.group({
third : ['', Validators.required],
second: [''],
first: [''],
four: ['']
}));
}
onSubmit() {
console.log(this.cities.value); // ['SF', 'NY']
console.log(this.form.value); // { cities: ['SF', 'NY'] }
}
setPreset() {
if(this.cities.length > 1){
this.cities.removeAt(this.cities.length-1);
}
}
}
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div formArrayName="cities">
<!-- <div *ngFor="let city of cities.controls; index as i">
<input [formControlName]="i" placeholder="City">
</div> -->
<div *ngFor="let city of cities.controls; index as i">
<div [formGroupName]="i">
<select formControlName="first">
<option [selected]="true">--Select--</option>
<option>A1</option>
<option>B1</option>
<option>C1</option>
</select>
<select formControlName="second">
<option [selected]="true">--Select--</option>
<option>A2</option>
<option>B2</option>
<option>C2</option>
</select>
IN : <input type="checkbox" formControlName="third" value="+">
Out :<input type="checkbox" formControlName="four" value="+">
</div>
</div>
</div>
<input type="button" name="" value="Enable integration" (click)="onSubmit()">
</form>
I'm not sure what your for loop is for in your onAddProperty and onDelProperty methods.
And if I understand correctly, your name and desg controls should be added to the fb.group defined in ngOnInit. Those will then be defined only one time. Only add to the createItem method those controls that you want repeated each time the user clicks the Add.
I have similar code that adds an address block. My code looks something like this:
#Component({
selector: 'my-signup',
templateUrl: './app/customers/customer.component.html'
})
export class CustomerComponent implements OnInit {
customerForm: FormGroup;
get addresses(): FormArray{
return <FormArray>this.customerForm.get('addresses');
}
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.customerForm = this.fb.group({
firstName: ['', [Validators.required, Validators.minLength(3)]],
lastName: ['', [Validators.required, Validators.maxLength(50)]],
addresses: this.fb.array([this.buildAddress()])
});
}
addAddress(): void {
this.addresses.push(this.buildAddress());
}
buildAddress(): FormGroup {
return this.fb.group({
addressType: 'home',
street1: '',
street2: '',
city: '',
state: '',
zip: ''
});
}
}
In the above example, the first name and last name appear only once, but the address fields are repeated each time the user clicks an add button.
For a full working example, check out this github: https://github.com/DeborahK/Angular2-ReactiveForms (specifically the Demo-Final folder)
UPDATE to include YOUR code:
Make the following change ... notice that everything you do not want repeated is in the ngOnInit. Everything you do want repeated is in the createItem.
ngOnInit() {
this.searchForm = this.fb.group({
name: '',
desg: '',
properties: this.fb.array([this.createItem()])
});
}
createItem(): FormGroup {
return this.fb.group({
social_media: '',
github:''
});
}
You also, of course, need to remove the fields you don't want repeated from the ngFor in your html. Just move them up above the ngFor.

Angular 2 - changes values from a parent component not reflecting in ngIf statement even though the child component actually gets the form

here is the parent component
import { Component, OnInit } from '#angular/core';
import {ViewEncapsulation} from '#angular/core';
#Component({
selector: 'app-bukkacorporateform',
templateUrl: './bukkacorporateform.component.html',
styleUrls: ['./bukkacorporateform.component.css'],
encapsulation: ViewEncapsulation.None
})
export class BukkacorporateformComponent implements OnInit {
constructor() { }
ngOnInit() {
}
check: number = 6;
indi = function individual(){
this.check = 4;
}
cliq = function clique(){
this.check = 5;
}
comp = function company(){
this.check = 6;
}
}
and here is the child component
import { Component, OnInit, Input, NgZone } from '#angular/core';
import {ViewEncapsulation} from '#angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule } from '#angular/forms';
import {Observable} from 'rxjs/Rx';
import {FormService} from '../../services/form.service';
import {CompaniesData} from '../../models/companies-data';
#Component({
selector: 'app-corporateform',
templateUrl: './corporateform.component.html',
styleUrls: ['./corporateform.component.css'],
encapsulation: ViewEncapsulation.None,
providers: [FormService]
})
export class CorporateformComponent implements OnInit {
#Input('data') type:number;
value: boolean = true;
subscriptionForm : FormGroup;
constructor(fb: FormBuilder, private formService: FormService){
this.subscriptionForm = fb.group({
'contactname' : '',
'phonenumber': '',
'type' : '',
'email' : '',
'companyname' : '',
'approxbudget' : '',
'approxbudget' : '',
'noofpeople' : '',
'eventtype': ''
})
}
submitForm(value: any):void{
value.type = this.type;
value.phonenumber += '';
value.phonenumber = '0' + value.phonenumber;
console.log(value);
}
}
when i submit the form the type variable is actually changing as expected, however the ngIf in the view is not responding to the same variable changes
and in the template
<div class="three-in" *ngIf="type == 6">
<!--<div class="form-group col-3 drop_fields">
</div>-->
<div class="form-group col-3">
<input class="form-control" id="corporate_lead_event_at" name="corporate_lead[event_at]" placeholder="Date of event" readonly="readonly" style="background-color: #fff;" type="text">
</div>
<div class="form-group col-3">
<input class="form-control" data-error="Invalid number" data-wrong="Invalid number" id="corporate_lead_budget" name="corporate_lead[budget]" placeholder="Approx Budget" type="number">
</div>
<div class="form-group col-3">
<input class="form-control" id="corporate_lead_number_of_people" name="corporate_lead[number_of_people]" placeholder="No. of people" type="number">
</div>
<div class="clear"></div>
</div>

Categories

Resources