angular2 using input to dynamically add to form model - javascript

This is to some extent a dupe of this question but hopefully less convoluted.
Is it possible to to add a completely new FormGroup via a form input where the new FormGroupName can be defined via input? If so which directives or functions would I use to achieve this?
User Case:
The user can define a new FormGroupName via input field and click to extend the form with the new FormGroup, then fill out values for that FormGroup’s FormControls.
1 User fills out an existing 'name' field as normal.
2 Types ‘foo’ into an input field
3 Clicks ‘add data group’ to create the new FormGroup 'foo'
4 The form reveals input fields for 'height' and 'weight' for the new FormGroup foo.
5 User fills out 'height' and 'weight' values for 'foo'
6 Repeats step 2-5 for 'bar'
7 Submits:
{
"name":"Data Form",
"foo":{
"height":"6.00",
"weight":"300"
},
"bar":{
"height":"5.11",
"weight":"260"
}
}
The FormControls in the added FormGroups will always be consistent.
So as in the above example always ‘weight’ and ‘height’.
The intention is to use the form as a basic UI to help generate some JSON data.
Thanks.

The solution I came up with was to use a dynamic [formGroupName] which references an array which is updated each time a new group is added
[formGroupName]="myGroupName[i]"
app.component.ts :
import { Component, OnInit } from '#angular/core';
import { Customer } from './customer.interface';
import { FormControl, FormBuilder, FormGroup, FormArray, Validators } from '#angular/forms';
#Component({
moduleId: module.id,
selector: 'my-app',
templateUrl: 'app.component.html',
})
export class AppComponent implements OnInit {
public myForm: FormGroup;
myGroupName = ['test'];
constructor(private _fb: FormBuilder) {
}
ngOnInit() {
this.myForm = this._fb.group({
myArray: this._fb.array([
this._fb.group({
test: this._fb.group({
name: ['test'],
title: [''],
link: [''],
cta: [''],
})
}),
])
});
}
initArray(nameObj:any) {
return this._fb.group({
[nameObj]: this._fb.group({
name: [nameObj],
title: [''],
link: [''],
cta: [''],
})
})
}
addArray(newName:string) {
const control = <FormArray>this.myForm.controls['myArray'];
this.myGroupName.push(newName);
control.push(this.initArray(newName));
document.getElementById('newName').value='';
}
removeDataKey(i: number) {
const control = <FormArray>this.myForm.controls['myArray'];
control.removeAt(i);
this.myGroupName.splice(i,1);
}
}
app.component.html:
<form [formGroup]="myForm" novalidate (ngSubmit)="save(myForm)">
<div formArrayName="myArray">
<div *ngFor="let myGroup of myForm.controls.myArray.controls; let i=index" class="panel panel-default">
<div [formGroupName]="i" class="panel-body">
<span *ngIf="myForm.controls.myArray.controls.length > 1"
(click)="removeDataKey(i)" class="glyphicon glyphicon-remove pull-right">
</span>
<h5 >Group {{i + 1 }}</h5>
<h3> {{myGroupName[i] }}</h3>
<!--[formGroupName]="myGroupName[i]"-->
<div [formGroupName]="myGroupName[i]" class="panel-body">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" formControlName="title" >
</div>
<div class="form-group">
<label>Link</label>
<input type="text" class="form-control" formControlName="link" >
</div>
<div class="form-group">
<label>Cta</label>
<input type="text" class="form-control" formControlName="cta" >
</div>
</div>
<!--[formGroupName]="myGroupName[i]"-->
</div>
<!--[formGroupName]="i" -->
</div>
</div>
<!-- myArray array-->
<div class="margin-20">
<input #newName
(keyup.enter)="addName(newName.value)"
type="text" style="width:30%" id="newName">
<a (click)="addArray(newName.value)" style="cursor: default" class="btn">Add Group +</a>
</div>
</form>
The dupe of this question is also answered here with related plunks

Related

How can i validade a button click through an array of inputs?

I have an array of inputs:
<div id="playerZone" *ngFor="let player of team;let i=index">
<div id="buttonZone">
<div class="buttonsAdd">
<mat-form-field appearance="outline" #f="ngForm">
<mat-label>Summoner Name</mat-label>
<label>
<input matInput placeholder="Placeholder"
(change)="updatePlayerSumonerName($event,i)">
</label>
</mat-form-field>
</div>
</div>
<button mat-raised-button routerLink="/waiting" [disabled]="" (click)="placeOnTheList()" hidden="">Waiting Room</button>
And a button that i only want to enable if all inputs are filled, and i dont know how to do that.
I need some guidance.
I could create variables that get to true when the input is written, but i know that is a better way of do that
updatePlayerSumonerName(name,i){
console.log(name.target.value);
this.team[i].summonerName = name.target.value;
}
You can achieve ur requirement as below.
Please check this Demo
in your app.component.ts,
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup, FormArray, Validators, FormBuilder } from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
players = [
'player1',
'player2',
'player3',
'player4'
];
constructor(private fb: FormBuilder) { }
form = this.fb.group({
team: new FormArray([])
});
ngOnInit() {
this.addPlayers(this.players);
}
get team(): FormArray {
return this.form.get('team') as FormArray;
}
onFormSubmit(): void {
for (let i = 0; i < this.team.length; i++) {
console.log(this.team.at(i).value);
}
}
addPlayers(players: string[]): void {
players.forEach(player => {
this.team.push(new FormControl(player, [Validators.required]));
});
}
}
and app.component.html,
<div class="container">
<br>
<form [formGroup]="form" (ngSubmit)="onFormSubmit()">
<div formArrayName="team">
<div class="form-group row">
<label for="commessa" class="col-sm-2 col-form-label">Team</label>
<div class="col-sm-10">
<ng-container *ngFor="let player of team.controls; index as idx">
<div class="row">
<div class="col-sm-8">
<input type="text" [ngClass]="{'error':player.invalid && player.touched}"
[formControlName]="idx" class="form-control" id="commessa">
</div>
</div>
</ng-container>
</div>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary" [disabled]="form.invalid">Save</button>
</div>
</form>
</div>

Delete button element from array loop Angular

I am trying to delete/remove the button from the array of 1st and 2nd index loop and display only at last index value or loop.
below is the code for approach:
import {
Component,
OnInit
} from '#angular/core';
import {
FormGroup,
FormControl
} from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
name = 'Angular';
userForm = new FormGroup({
name: new FormControl(),
age: new FormControl()
});
masterData = [{
name: 'alex',
age: 20,
button: 'no'
},
{
name: 'bony',
age: 21,
button: 'no'
},
{
name: 'acute',
age: 23,
button: 'yes'
}
]
ngOnInit() {
}
html:
<div *ngFor="let data of masterData">
<form [formGroup]="userForm" (ngSubmit)="onFormSubmit()">
Name: <input formControlName="name" placeholder="Enter Name"> Age: <input formControlName="age" placeholder="Enter Age">
<button type="submit">Submit</button>
<button type="button">display at last </button>
</form>
</div>
The above is the code in which the the array of object is integrated, where the 'display at last" button should be displayed only for the last object of the array.
The approached is followed is to get the element-ref of the button from dom but it dint work. Please help me to solve this issue
For better understanding here is a link of code:
https://stackblitz.com/edit/angular-chdfdh?file=src%2Fapp%2Fapp.component.ts
You can get the index from the *ngFor, like so:
<div *ngFor="let data of masterData; let last = last">
<form [formGroup]="userForm" (ngSubmit)="onFormSubmit()">
Name: <input formControlName="name" placeholder="Enter Name"> Age: <input formControlName="age" placeholder="Enter Age">
<button type="submit">Submit</button>
<button type="button" *ngIf="last">display at last </button>
</form>
</div>
So what is happening is that I am adding a variable called last to the last item of the for loop. Then only displaying the button if the variable is true, i.e. last item.
EDIT
I see that there is also a variable in the masterData. You can just use the variable from that to display the button.
For example:
<div *ngFor="let data of masterData; let last = last">
...
<button type="button" *ngIf="data.button === 'yes'">display at last </button>
</form>
</div>

Button does not disable on change in Angular

I have this button that will be disabled everytime that the form is invalid
<button [disabled]="createForm.invalid" type="submit" class="btn btn-primary pull-right button-spinner" tabindex="0" ><span>NEXT</span></button>
but it seems like the validation only effect everytime I blur on a form control instead on change.
I also added this on ngOnit and changed the [disabled]="isNextDisabled" but it seems that the state will change on change too
this.createForm.valueChanges
.subscribe(formValue => {
this.isNextDisabled = !this.createForm.valid;
}
);
The initialization of the form:
this.createForm = this.formBuilder.group({
formText: ['', Validators.required],
list: ['', Validators.required]
)};
<!-- disable the button -->
<button>
Disable Button
</button>
<!-- enable the button -->
<button>
Enable Button
</button>
<!-- the target button -->
<button disabled="disabled">
I've been clicked {{count}} times.
</button>
follow the link below for getting the detail:
https://blogs.msdn.microsoft.com/laurieatkinson/2016/09/12/using-data-binding-with-angular-2-to-disable-a-button/
I will try to resolve your issue with one example as I did. Follow this you can do.
test.html
<form [formGroup]="changePasswordForm" (ngSubmit)="changePassword()">
<ion-item color="border-text">
<ion-input type="password" placeholder="Existing Password"
formControlName="existing_pass"></ion-input>
</ion-item>
<ion-item no-lines *ngIf="existingPass.invalid && existingPass.touched">
<ion-label style="color:#F53D3D" no-margin stacked>Existing Password Required</ion-label>
</ion-item>
<div class="buttonClass">
<button [disabled]="!isReadyToSave" ion-button round color="primary" block
style="margin-top: 20px !important;">Save
</button>
</div>
</form>
test.ts
import {FormBuilder, FormGroup, Validators} from '#angular/forms';
changePasswordForm: FormGroup;
isReadyToSave: boolean = false;
constructor(formBuilder: FormBuilder,) {
this.changePasswordForm = formBuilder.group({
existing_pass: ['', Validators.required]
});
this.changePasswordForm.valueChanges.subscribe((v) => {
this.isReadyToSave = this.chanegPasswordForm.valid;
});
}
You can use two operations
<button [disabled]='createForm.invalid || isNextDisabled'>Submit</button>
And use what you have tried to disable when valueChanges
this.createForm.valueChanges.subscribe((v) => {
this.isNextDisabled = !this.createForm.valid;
});
Working Stackblitz
Actually you may not need to separately look for valueChanges. Here is a code
HTML
<form [formGroup]="createForm">
<input formControlName='formText'>
<input formControlName='list'>
<button [disabled]="createForm.invalid"
type="submit"
class="btn btn-primary pull-right button-spinner"
tabindex="0" >
<span>NEXT</span>
</button>
</form>
component.ts
import { Component, OnInit } from "#angular/core";
import {
FormsModule,
ReactiveFormsModule,
FormGroup, FormBuilder, Validators
} from "#angular/forms";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
name = "Angular";
createForm: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.createForm = this.formBuilder.group({
formText: ["", Validators.required],
list: ["", Validators.required]
});
}
}
Stackblitz DEMO
You can achieve this by checking your formGroup is valid or not with the attribute [disabled], check below for more clarification
TS:
// method which will create the FormGroup
private createMyForm() {
this.myForm = new FormGroup({
FormFiled1 : new FormControl('', [Validators.required, Validators.maxLength(200)]),
FormFiled2 : new FormControl('', []),
FormFiled3 : new FormControl('', [Validators.required])
});
}
[Validators.required] this will make the formfield required, as soon as you dont enter anything into the field the form will invalid
hence you have to check form is valid or not by using below code
HTML:
// set [disabled] value as true or false to disable or enable the button
<button type="submit" [disabled]="myForm.invalid" (click)="MyFunction()">Submit</button>
Demo
Yes, using onchange disables or enables when the focus is out from the input field.
For this you need to use ngModelChange, then it will enable/disable the button when you're in the input field.
This method is generally used for only a single input field like confirmation pop with input field for deleting, suspended etc
<mat-form-field appearance="outline">
<mat-label>Please Confirm</mat-label>
<input matInput name="value" [(ngModel)]='value' placeholder="Enter {{confirmValue}}" (ngModelChange)="onChange($event)"
autocomplete="off">
</mat-form-field>
<button mat-raised-button class="mat-accent mr-16" (click)="onSubmit()" [disabled]="confirmDisable">
<mat-icon>done</mat-icon>Confirm
</button>
TS
confirmDisable: boolean = false;
value!: string;
onChange($event: any) {
this.confirmDisable= this.value === this.confirmValue ? false : true;
}
onSubmit() {
//now confirm, we can proceed further
}

Angular 5 Reactive Forms : There is no directive with "exportAs" set to "ngModel"

I'm working on angular 5 Reactive Forms :
I had this error :
There is no directive with "exportAs" set to "ngModel"
I saw in others forums that problem can be for many reasons :
misspelling in the HTML template , forgetting to import "FormsModule" or "ReactiveFormsModule", ....
I checked my code but i didn't find the issue
Can you Help me please !!!
Console error :
There is no directive with "exportAs" set to "ngModel" ("
[(ngModel)]="user.FirstName"
formControlName="FirstName"
[ERROR ->]#FirstName="ngModel" />
<label for="firstName">{{ 'FIRST_NAME' | translate:param}}</label>")
: ng:///AppModule/LoginComponent.html#12:15
app.module.ts:
//angular moudel
import { NgModule } from '#angular/core';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
....
#NgModule({
declarations: [
.....
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
...
AppRoutingMoudel,
],
...
})
LoginComponent.ts
import { Component, OnInit } from '#angular/core';
import { User } from './../../../model/user';
import {FormBuilder,FormGroup,FormControl,Validators,NgForm} from '#angular/forms'
....
export class LoginComponent implements OnInit {
user : User;
userLoginForm: FormGroup;
constructor(private userLoginFormBuilder:FormBuilder) {
this.user = new User ("TestName", "Yagmi",
"TestName#Yagmi.com", "esay", "esay");
this.userLoginForm = this.userLoginFormBuilder.group({
FirstName: new FormControl (this.user.FirstName,
[Validators.minLength(4),])
});
}
}
LoginComponent.Html
<form class="col s12" [formGroup]="userLoginForm" (ngSubmit)="saveUserLogin()">
<div class="row">
<div class="input-field col s12 m6">
<input id="firstName"
type="text"
class="validate"
[(ngModel)]="user.FirstName"
formControlName="FirstName"
#FirstName="ngModel" />
<label for="firstName">{{ 'FIRST_NAME' | translate:param }}</label>
<p class="data-error data-validation" *ngIf="FirstName.errors?.minlength">
min length is 4 caracters.
</p>
<p class="data-error data-validation" *ngIf="FirstName?.touched">
touched.
</p>
</div>
</div>
</form>
user.ts
export class User {
constructor(
public FirstName: string,
public LastName: string,
public Email: string,
public Passeword: string,
public ConfirmPasseword: string
)
}
I find where is my error I used template driven and reactive forms together
this why i had error (after i read the comment of Alex )
the solution is just to remove all template driven from my Html template
<form class="col s12" [formGroup]="userLoginForm" (ngSubmit)="saveUserLogin()">
<div class="row">
<div class="input-field col s12 m6">
<input id="firstName"
type="text"
class="validate"
//[(ngModel)]="user.FirstName" ==> to remove template driven
formControlName="FirstName" //==> to keep reactive forms
// #FirstName="ngModel" ===> to remove template driven
/>
<label for="firstName">{{ 'FIRST_NAME' | translate:param }}</label>
<p class="data-error data-validation" *ngIf="FirstName.errors?.minlength">
min length is 4 caracters.
</p>
<p class="data-error data-validation" *ngIf="FirstName?.touched">
touched.
</p>
</div>
</div>
</form>
also im my
LoginComponent.ts
import {FormBuilder,FormGroup,FormControl,Validators} from '#angular/forms'
==> remove import NgForm no need with reactive forms
In the line
#FirstName="ngModel"
The component referenced needs to have defined "exportAs" value.
For example
#Directive({
selector: '[tooltip]',
exportAs: 'tooltip'
})
#FirstName="tooltip"
https://netbasal.com/angular-2-take-advantage-of-the-exportas-property-81374ce24d26

I can't make an input be required with Angular2

I'm starting with Angular and I'm on a project where I have to validate the inputs so they can't be left clear, every input must me completed.
It's an html and we have a .ts file.
This is an extract of the html:
<div class="form-group">
<input type="text"
class="form-control"
id="factory"
[(ngModel)]="factory.company">
</div>
I need to validate this factory input but when I was watching tutorials all I needed to do was to write 'required' inside the <input> and that was it but I had a <form> and every input was inside this form, and this html doesn't have a <form> and when I put one the design was horrible and I couldn't work.
Here is an example using required fields (in login page) :
<form [formGroup]='loginForm' (submit)="login(loginForm.value)">
<div class="col-md-6">
<div class="login-mail">
<input type="text" placeholder="Email" formControlName="email" required="">
<i class="fa fa-envelope"></i>
</div>
<div class="login-mail">
<input type="password" placeholder="Password" formControlName="password" pattern=".{8,20}" required="">
<i class="fa fa-lock"></i>
</div>
</div>
<div class="col-md-6 login-do">
<label class="hvr-shutter-in-horizontal login-sub">
<input type="submit" value="login" >
</label>
</div>
<div class="clearfix"> </div>
</form>
in the login.component.ts , u should make some changes:
1) import some modules :
import { FormBuilder, FormGroup, Validators} from '#angular/forms';
2) in oninit function :
loginForm: FormGroup;
constructor(private fb : FormBuilder) {}
ngOnInit(){
this.loginForm = this.fb.group({
email : ["", Validators.required],
password : ["", Validators.required]
});
}
Hope that helps u :)
I would think that you should be able to add a form element. However, if you cannot as you have said then you can add the ngForm directive onto any element to achieve the same behavior as having the form element.
See this plunker for examples using ReactiveFormsModule and FormsModule:
Plunker
//our root app component
import {Component, OnInit, NgModule} from '#angular/core'
import {ReactiveFormsModule, FormsModule, FormControl, Validators} from '#angular/forms'
import {BrowserModule} from '#angular/platform-browser'
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<div class="form-group">
<label>Model Driven Form</label>
<input type="text"
class="form-control"
id="companyModel"
[formControl]="companyModel">
<span [hidden]="companyModel.valid || companyModel.pristine">REQUIRED!</span>
</div>
<div class="form-group" ngForm #myForm="ngForm">
<label>Template Driven Form</label>
<input type="text"
class="form-control"
name="companyTemplate"
ngModel
id="companyTemplate"
#companyTemplate="ngModel"
required>
<span [hidden]="companyTemplate.valid || companyTemplate.pristine">REQUIRED!</span>
</div>
</div>
`,
})
export class App implements OnInit {
name:string;
companyModel: FormControl
constructor() {
this.name = 'Form Validation Demo'
}
ngOnInit() {
this.companyModel = new FormControl('', Validators.required)
}
}
#NgModule({
imports: [ BrowserModule, ReactiveFormsModule, FormsModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}

Categories