I am working in Angular project and I am creating form using Reactive approach. I want to create repeater control in child component. Please find the code below.
MainComponent.html
<div [formGroup]="myForm">
<input type="text" class="form-control" placeholder="Name"
formControlName="name" />
<div formArrayName="measurements">
<div *ngFor="let ctrl of myForm.get('measurements').controls; let i=index">
<div [formGroupName]="i">
<measurement-edit [measurementForm]="myForm.controls.measurements.controls[i]">
</measurement-edit>
</div>
</div>
</div>
<div formArrayName="thresholds">
<div *ngFor="let ctrl of myForm.get('thresholds').controls; let i = index">
<div [formGroupName]="i">
<threshold-edit
[thresholdForm]="myForm.controls.thresholds.controls[i]"
[valueType]="myForm.controls.valueType.value">
</threshold-edit>
</div>
</div>
</div>
</div>
ThresholdEditComponent.html
<div [formGroup]="thresholdForm">
<input type="checkbox"
[value]="isThresholdRequired"
formControlName="isThresholdRequired">
<!-- Here I want to use repeater controls and these controls should be accessed from parent (main) commponent -->
<div formArrayName="redThresholdItems">
<div *ngFor="let itemRow of thresholdForm.get('redThresholdItems').controls; let j = index;">
<div [formGroupName]="j">
<input type="text"
class="form-control txt-thr-val-width"
formControlName="thresholdValue1" />
</div>
</div>
</div>
</div>
MainComponent.ts
ngOnInit(): void {
this.createForm();
}
createMetricForm(): void {
this.myForm = this.fb.group({
name: ['', Validators.required],
easurements: this.fb.array([
this.initMeasurements()
]),
thresholds: this.fb.array([
this.initThresholds()
])
});}
initMeasurements(): FormGroup {
return this.fb.group({
isDerivedMetric: [false],
baselineMetricId: [''],
metricCalculation: [''],
metricSelection: ['']
});}
initThresholds(): FormGroup {
return this.fb.group({
isThresholdRequired: [false],
redThresholdItems: this.fb.array([
this.initThresholdRatings()
])
});}
initThresholdRatings(): FormGroup {
return this.fb.group({
thresholdValue1Operator: ['-1'],
thresholdValue1: [''],
compoundOperator: ['-1'],
thresholdValue2Operator: ['-1'],
thresholdValue2: ['']
});}
Please help me to create repeater controls in child component using the above code.
Related
I have a problem in input text, when I write in one input it changes in the other input as well ! how can I fix that
here is it my code:
<mat-expansion-panel class="childPanel" [expanded]="true" hideToggle disabled style=" background:#F2EDFF;" *ngFor="let i of listSousCategorie" >
<div class="childPanel_body2 mat-badge-large" fxLayout="row" fxFlex fxLayoutAlign="start center" >
<div style="padding-left:1em" fxFlex="grow" >{{i.libelle}} {{i.id}}</div>
<mat-form-field>
<form [formGroup]="form" (ngSubmit)="onSubmit(i.id)">
<mat-label>Note</mat-label>
<input type="text" matInput formControlName="Note" [(ngModel)]="Note" >
</form>
</mat-form-field>
</div>
</mat-expansion-panel>
TS File:
Note:string;
ngOnInit() {
this.form = this.formBuilder.group({
Note: [''],
});
}
onSubmit(id:number) {
let obj = this.form.value;
obj.Note = this.Note;
console.log(this.Note);
this.LExamenSousCategorieService
.updateSouCategorie(obj,id)
.then(res => {});
}
<mat-expansion-panel class="childPanel" [expanded]="true" hideToggle disabled style=" background:#F2EDFF;" *ngFor="let i of listSousCategorie" >
<div class="childPanel_body2 mat-badge-large" fxLayout="row" fxFlex fxLayoutAlign="start center" >
<div style="padding-left:1em" fxFlex="grow" >{{i.libelle}} {{i.id}}</div>
<mat-form-field>
<mat-label>Note</mat-label>
<input type="text" matInput [(ngModel)]="i.Note">
</mat-form-field>
<button type="button" (click)="onSubmit(i.id)">
</div>
</mat-expansion-panel>
ts
// All commented lines are not needed
// Note:string;
// ngOnInit() {
// this.form = this.formBuilder.group({
// Note: [''],
// });
// }
onSubmit(id:number) {
// let obj = this.form.value;
// obj.Note = this.Note;
console.log(this.listSousCategorie);
this.LExamenSousCategorieService
.updateSouCategorie(i.Note,id)
.then(res => {});
}
This is still not the best solution for you, but solves your problem. A better solution would require an extend refactoring.
Can someone explain what this actually means? I am new to Angular and currently using Angular 8 and this appears on my console.
ERROR TypeError: Cannot read property 'invalid' of undefined
at Object.eval [as updateDirectives] (RegisterComponent.html:10)
at Object.debugUpdateDirectives [as updateDirectives] (core.js:45259)
at checkAndUpdateView (core.js:44271)
at callViewAction (core.js:44637)
at execComponentViewsAction (core.js:44565)
at checkAndUpdateView (core.js:44278)
at callViewAction (core.js:44637)
at execEmbeddedViewsAction (core.js:44594)
at checkAndUpdateView (core.js:44272)
at callViewAction (core.js:44637)
Here's my sourcode which implies the page that have the errors
register.component.ts
import { authTkn } from './../shared/model/loginDetails';
import { MahasiswaApiService } from './../shared/services/mahasiswa-api.service';
import { Component, OnInit } from '#angular/core';
import { Router, RouterModule } from '#angular/router';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { first } from 'rxjs/operators';
import * as CryptoJS from 'crypto-js';
#Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss']
})
export class RegisterComponent implements OnInit {
public authTkn: authTkn = null;
registerForm = this.fb.group({
user_name: ['', Validators.required],
fullname: ['', Validators.required],
telepon: ['', Validators.required],
email: ['', Validators.required],
alamat: ['', Validators.required],
birthdate: ['', Validators.required],
foto_profil: ['', Validators.required],
password: ['', Validators.required],
});
constructor(private mahasiswaApi: MahasiswaApiService, private route: Router, private fb: FormBuilder) { }
ngOnInit() {
}
onSubmit() {
this.registerForm.controls.password.patchValue(
CryptoJS.SHA512(this.registerForm.value.password).toString()
);
console.log(this.registerForm.value);
this.mahasiswaApi.postUserRegister(this.registerForm.value).subscribe(
res => {
console.log(res);
this.authTkn = res;
console.log(this.authTkn);
localStorage.setItem('token', this.authTkn.token);
this.mahasiswaApi.getCurrentToken();
this.route.navigate(['/home']);
alert(this.authTkn.info);
},
error => {
console.log(error);
alert(error.error.message);
}
);
}
}
register.component.html
<div class="login-form">
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<h2 class="text-center"> Register </h2>
<!-- Register Username -->
<div class="form-group">
<label for="user_name"> Username </label>
<input type="text" class="form-control" name="Username" formControlName="user_name" placeholder="text">
<div class="alert alert-danger" *ngIf="user_name.invalid && user_name.dirty" #myInsertRemoveTrigger>Username Must Be filled</div>
</div>
<!-- Register Nama Lengkap -->
<div class="form-group">
<label for="nama lengkap"> Nama Lengkap </label>
<input type="text" class="form-control" name="Nama Lengkap" formControlName="fullname" placeholder="text">
<div class="alert alert-danger" *ngIf="fullname.invalid && fullname.dirty" #myInsertRemoveTrigger> Please fill your complete name here</div>
</div>
<!-- Register Nomor Telepon -->
<div class="form-group">
<label for="nomor_telepon"> Nomor Telepon </label>
<input type="text" class="form-control" name="Nomor Telepon" formControlName="telepon" placeholder="text">
</div>
<!-- Register Email -->
<div class="form-group">
<label for="email"> Email </label>
<input type="email" class="form-control" name="Email" formControlName="email" placeholder="email">
</div>
<!-- Register Tanggal Lahir -->
<div class="form-group">
<label for="Tanggal_lahir"> Tanggal Lahir </label>
<input type="date" class="form-control" name="Tanggal Lahir" formControlName="birthdate" placeholder="date">
</div>
<!-- Register Foto -->
<div class="form-group">
<label for="Foto_Profil"> Foto Profil </label>
<input type="file" class="form-control" name="Foto Profil" formControlName="foto_profil" placeholder="file">
</div>
<!-- Register Password -->
<div class="form-group">
<label for="Password"> Password </label>
<input type="password" class="form-control" name="Password" formControlName="password" placeholder="password">
<div class="alert alert-danger" *ngIf="password.invalid && password.dirty" #myInsertRemoveTrigger>Password Must Be filled</div>
</div>
<!-- Button shit -->
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block" [disabled]="!registerForm.valid"> Register </button>
</div>
<div class="clearfix">
<label class="pull-left checkbox-inline"><input type="checkbox" formControlName="remember_me"> Remember me</label>
</div>
</form>
</div>
Maybe I had some variable definition problem, but I did actually define arrays of used variables for my form at the first part of my ts. Maybe I did lost something, what is that?
You'll need to access the form control on the form object like this: registerForm.get('user_name').invalid and so on, for each property (such as dirty) and for each control in the form.
Another alternative is to create a getter in your component class for each form control:
get user_name() {
return this.registerForm.get('user_name');
Then you can keep your HTML as it is currently.
If you look at the example here: https://angular.io/guide/form-validation#built-in-validators they say:
This example adds a few getter methods. In a reactive form, you can always access any form control through the get method on its parent group, but sometimes it's useful to define getters as shorthands for the template.
In reference to getters for the form controls.
The HTML *ngIf can't locate user_name.invalid (same for the fullname.invalid) as they aren't variables contained in the component. You have them listed in the formGroup but that isn't the same thing as declaring a user_name variable at the top of the class.
To make user_name, fullname and password available you would need to declare them like so:
user_name: string;
fullname: string;
password: string;
And then populate them in your code. This can get messy when you have essentially the same data now residing in two places.
Alternatively, you could use a function to return the value of control from the formGroup:
getUserName() {
return this.formGroup.controls['user_name'].value;
}
But the main thing to take away, is that controls specified in a formGroup aren't available to be displayed the same way a variable accessed via this is.
Add #fullname = "ngModel" to the input you would like to reference. Your div for the validation doesn't know fullname.
I have a FormGroup which has three FormControl fields and one FormArray fields as shown in the figure below. The requirement is to take the manager name from user and once add button is pressed, manager details should be displayed in table. In table a remove button is provided, when remove button is pressed manager should be removed form the table and list. When the form is submitted list of managers should be saved. Everything works fine except formArray logic.
I tried to find a solution to this online (followed various links:- https://alligator.io/angular/reactive-forms-formarray-dynamic-fields/,
Angular 4 Form FormArray Add a Button to add or delete a form input row), but did not helped much. There is not much material on how to store formArray in formGroup. Please suggest.
Below is my code, please have a look:-
1. manager-create-modal.component.html
<div>
<form [formGroup]="createForm" (ngSubmit)="onFormCreation()">
<div class="row">
<div class="column">
<div class="form-inline">
<div class="form-group">
<label for="remote_access_method">Remote Access Method: <font color="orange"> *</font> </label>
<input type="text" size='38' class="form-control" formControlName="remote_access_method" >
</div>
</div>
<br>
<div class="form-inline">
<div class="form-group">
<label for="status">Current Status: <font color="orange"> *</font> </label>
<input type="text" size='38' class="form-control" formControlName="status">
</div>
</div>
<br>
<div class="form-inline">
<div class="form-group">
<label for="secregid">Registration ID:<font color="orange"> *</font> </label>
<input type="text" size='38' class="form-control" formControlName="secregid">
</div>
</div>
<br><br>
<div class="form-inline">
<div class="form-group">
<br><br>
<div formArrayName="manager_formArray">
Enter name: <input type="text" class="form-control" formControlName="MgrName" size='50' >
<button type="button" class="btn btn-primary btn-sm" (click)="addPM()">Add</button>
<br><br>
<table class="table table-hover">
<tr><th>#</th><th>Manager Name</th><th>Remove</th></tr>
<tr *ngFor="let pm of createForm.get('manager_formArray').value; let id = index">
<td>{{id+1}}</td>
<td>{{pm.MgrName}}</td>
<td>
<span class="table-remove">
<button type="button" class="btn btn-primary btn-sm" (click)="removeMgr(id)">Remove</button>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<br>
</div>
<br><br>
<div class="form-group">
<button class="btn btn-primary">Submit</button>
</div>
</form>
</div>
2. manager-create-modal.component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, FormArray, FormControl, Validators } from '#angular/forms';
#Component({
selector: 'app-manager-create-modal',
templateUrl: './manager-create-modal.component.html',
styleUrls: ['./manager-create-modal.component.css']
})
export class ManagerCreateModalComponent implements OnInit {
createForm: FormGroup;
manager_formArray: FormArray;
remote_access_method: FormControl;
status: FormControl;
secregid: FormControl;
constructor(private formBuilder: FormBuilder) { }
createFormControls(){
this.remote_access_method = new FormControl('');
this.status = new FormControl('');
this.secregid = new FormControl('');
this.manager_formArray = new FormArray([ this.createItem() ]);
}
createItem(): FormGroup {
return this.formBuilder.group({
MgrName: ''
});
}
createFormVariables(){
this.createForm = new FormGroup({
remote_access_method : this.remote_access_method,
status : this.status,
secregid : this.secregid,
manager_formArray: this.manager_formArray,
})
}
ngOnInit() {
this.createFormControls();
this.createFormVariables();
}
addPM(mgr: any): void {
console.log("inside addPM");
this.manager_formArray.push(this.formBuilder.group({MgrName:''}));
console.log("list after addition:"+this.manager_formArray.value);
for(let i = 0; i < this.manager_formArray.length; i++) {
console.log(this.manager_formArray.at(i).value);
}
}
get managerFormArray() {
return this.manager_formArray.get('MgrName') as FormArray;
}
onFormCreation(){
console.log("success")
}
}
The manager name is not displayed in the table and I keep on getting below error:-
ERROR Error: Cannot find control with path: 'manager_formArray ->
MgrName' at _throwError (forms.js:1731) at setUpControl
(forms.js:1639) at
FormGroupDirective.push../node_modules/#angular/forms/fesm5/forms.js.FormGroupDirective.addControl
(forms.js:4456) at
FormControlName.push../node_modules/#angular/forms/fesm5/forms.js.FormControlName._setUpControl
(forms.js:4961) at
FormControlName.push../node_modules/#angular/forms/fesm5/forms.js.FormControlName.ngOnChanges
(forms.js:4911) at checkAndUpdateDirectiveInline (core.js:9031)
at checkAndUpdateNodeInline (core.js:10299) at
checkAndUpdateNode (core.js:10261) at debugCheckAndUpdateNode
(core.js:10894) at debugCheckDirectivesFn (core.js:10854) inside
addPM manager-create-modal.component.ts:50 list after
addition:[object Object],[object Object]
manager-create-modal.component.ts:53 {MgrName: ""}
manager-create-modal.component.ts:53 {MgrName: ""}
I even don't understand why elements are not getting added to manager_formArray. Please help me out.
You have a few issues. First of all, it is better to move the Input which adds more FormGroups to your FormArray outside of the <div formArrayName="manager_formArray">- element. I created a new FormControl this.mgrNameInput = new FormControl(''); for this reason (see StackBlitz for more details).
You also need to add the message to the new entry when you press the Add-button, calling the addPM()-method:
addPM(){ // removed the argument, using the controller inside the method instead.
this.manager_formArray.push(this.formBuilder.group({MgrName:this.mgrNameInput.value}));
this.mgrNameInput.reset(); // reset the input field.
}
I also added the remove-method when removing an entry.
removeMgr(index: number){
this.manager_formArray.removeAt(index);
}
Please check the StackBlitz for the complete example
I am trying to make a specific form field in my overall form dynamic so that x amount of objects can be added to the array of that field.
However, every time the page inits I get a
Error: Cannot find control with path: 'media -> '
This is my form.ts
this.cardForm = new FormGroup({
'title': new FormControl(cardTitle),
media: this._fb.array([
this.initMedia(),
]),
'links': new FormGroup({
'news': new FormControl(news),
}),
initMedia() {
return this._fb.group({
type: new FormControl(),
raw: new FormControl(),
primary: new FormControl(),
thumbs: this._fb.group({
default: new FormControl()
})
})
}
addMedia(){
const control = <FormArray>this.cardForm.controls['media'];
control.push(this._fb.control(['']));
}
removeMedia(i: number){
const control = <FormArray>this.cardForm.controls['media'];
control.removeAt(i);
}
this is my form.html:
<div class="row">
<div class="col-xs-12">
<form [formGroup]="cardForm" (ngSubmit)="onSubmit(cardForm.value)">
<div class="row">
<div class="col-xs-12">
<button
type="submit"
class="btn btn-success">
Update Card</button>
<button
type="button"
class="btn btn-danger"
(click)="onCancel()">
Cancel</button>
</div>
</div>
<div formArrayName="media">
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<div *ngFor= "let media of cardForm.controls.media.controls; let i=index">
<span>Media {{i + 1}}</span>
<span *ngIf="cardForm.controls.media.controls.length > 1" (click)="removeMedia(i)"></span>
</div>
<div [formGroupName]="i">
<div>
<label>Url</label>
<md-input-container class="mdcontainer">
<input mdInput placeholder="Media Url" type="text" formControlName="raw">
</md-input-container>
</div>
</div>
</div>
</div>
</div>
</div>
And the media[] looks like this:
media: [
{
raw:'string',
primary: boolean,
type: 'string',
thumb: {
default: 'string'
{
}
]
What am I missing/doing wrong here for that error to come up?
Any help/tips/suggestions would be much appreciated.
[formGroupName]="i" should be inside of *ngFor. In this case i variable will have non undefined value
<div *ngFor="let media of cardForm.controls.media.controls; let i=index">
<span>Media {{i + 1}}</span>
<span *ngIf="cardForm.controls.media.controls.length > 1" (click)="removeMedia(i)"></span>
<div [formGroupName]="i">
<div>
<label>Url</label>
<md-input-container class="mdcontainer">
<input mdInput placeholder="Media Url" type="text" formControlName="raw">
</md-input-container>
</div>
</div>
</div>
Plunker Example
You could try the get method instead. I think it is a bit more user-friendly:
cardForm.get('media')
Check it out in the docs here: https://angular.io/guide/reactive-forms#inspect-formcontrol-properties
I've been working with reactive form like I show in the link
https://plnkr.co/edit/ApCn3YicMjfm2vhSOudj?p=preview
this is my form
<div *ngFor="let item of data; let index = index">
<form novalidate (ngSubmit)="onSubmit(user)" [formGroup]="user">
<label>
<span>Full name</span>
<input type="text" placeholder="Name" formControlName="name">
</label>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('required')">
Name is required
</div>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('minlength')">
Minimum of 2 characters
</div>
<div formGroupName="account">
<label>
<span>Email address</span>
<input type="email" placeholder="Email" formControlName="email">
</label>
<div class="error" *ngIf="user.get('account').get('email').hasError('required') && user.get('account').get('email').touched">
Email is required
</div>
<label>
<span>Confirm address</span>
<input type="email" placeholder="Address" formControlName="confirm">
</label>
<div class="error" *ngIf="user.get('account').get('confirm').hasError('required') && user.get('account').get('confirm').touched">
Confirming email is required
</div>
</div>
<button type="submit" [disabled]="user.invalid">Sign up</button>
</form>
</div>
but my problem is that I have an ngFor, every time I submit the form it push the data to array.
How can I do if I want for example submit my first array and push the data to position 0 of my data array, if I submit my second form, it will push the data to position 1
But my second form should be empty
maybe you wanna using form array like this:
#Component({
selector: 'signup-form',
template: `
<form novalidate (ngSubmit)="onSubmit()" [formGroup]="users">
<div formArrayName="data">
<ng-container *ngFor="let user of fData.controls; index as i">
<div [formGroupName]="i">
<label>
<span>Full name</span>
<input type="text" placeholder="Name" formControlName="name">
</label>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('required')">
Name is required
</div>
<div class="error" *ngIf="user.get('name').touched && user.get('name').hasError('minlength')">
Minimum of 2 characters
</div>
<div formGroupName="account">
<label>
<span>Email address</span>
<input type="email" placeholder="Email" formControlName="email">
</label>
<div
class="error"
*ngIf="user.get('account').get('email').hasError('required') && user.get('account').get('email').touched">
Email is required
</div>
<label>
<span>Confirm address</span>
<input type="email" placeholder="Address" formControlName="confirm">
</label>
<div
class="error"
*ngIf="user.get('account').get('confirm').hasError('required') && user.get('account').get('confirm').touched">
Confirming email is required
</div>
</div>
<button type="submit" [disabled]="user.invalid">Sign up</button>
</div>
</ng-container>
</div>
</form>
`
})
export class SignupFormComponent implements OnInit {
user: FormGroup;
users: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.user = this.buildGroup();
this.users = this.fb.group({
data: this.fb.array([this.user])
});
}
get fData() {
return this.users.get('data') as FormArray;
}
buildGroup() {
return this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
account: this.fb.group({
email: ['', Validators.required],
confirm: ['', Validators.required]
})
});
}
onSubmit() {
this.fData.push(this.buildGroup());
const {valid, value} = this.fData;
console.log(valid, value);
}
}
basically, we're using FormArray to handle array of data, then loop through it.
in template, each time you loop through array item, Angular will store current AbstractControl in index variable (in above is i).
you can see form in action here: https://plnkr.co/edit/E5Qzm85LksSCZAloXZz5?p=preview
The API document here: https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html
you can use at, removeAt, etc to access or delete at specific index.