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>
Related
I've created the nested FormGroup (questionnaire) which contains a FormArray (section) which contains another FormGroup (creatSection()) that contains another FormArray (question) in which we have another formgroup (creatQuestion()) in which we have another formarray(answer) that has a formgroup (creatAnswer())
Everything appears fine unless I want to add another question or another answer nothing the addSection works fine
so the creation is ok but the problem in the add.
what I'm thinking about is when you create a question it doesn't know to where it's going to be put in but I couldn't figure out how to fix it
my questionnaire.ts looks like this
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, FormArray } from '#angular/forms'
#Component({
selector: 'app-questionnaire',
templateUrl: './questionnaire.component.html',
styleUrls: ['./questionnaire.component.css']
})
export class QuestionnaireComponent implements OnInit {
questionnaire:FormGroup;
section:FormArray;
question:FormArray;
answer:FormArray;
constructor(private fb:FormBuilder) { }
ngOnInit() {
this.questionnaire=this.fb.group({
questionnaireName: [''],
section : this.fb.array([this.creatSection()])
})
}
creatSection():FormGroup{
return this.fb.group({
sectionName:[''],
question:this.fb.array([this.creatQuestion()])
})
}
addSection(){
this.section=this.questionnaire.get('section') as FormArray;
this.section.push(this.creatSection());
}
creatQuestion():FormGroup{
return this.fb.group({
questionName:[''],
type:[''],
answer:this.fb.array([this.creatAnswer()])
})
}
addQuestion(){
this.question=this.creatSection().get('question') as FormArray;
this.question.push(this.creatQuestion());
}
creatAnswer():FormGroup{
return this.fb.group({
id:[''],
answerName:['']
})
}
addAnswer(){
this.answer=this.creatQuestion().get('answer') as FormArray;
this.answer.push(this.creatAnswer());
}
onSubmit(){
console.log(this.questionnaire.value);
}
}
and the questionnaire.html is like this
<h1>questionnaire bla bla</h1>
<!-- <app-section></app-section> -->
<form [formGroup]="questionnaire" (ngSubmit)="onSubmit()">
<input type="text" placeholder="questionnaire..." formControlName="questionnaireName">
<ng-container formArrayName="section">
<div [formGroupName]="i" *ngFor="let ques of questionnaire.get('section').controls;let i = index;">
<input placeholder="section..." formControlName="sectionName" type="text">
<button (click)="addSection()">add section</button>
<ng-container formArrayName="question">
<div [formGroupName]="j" *ngFor="let sec of creatSection().get('question').controls;let j=index;">
<input placeholder="question..." formControlName="questionName" type="text">
<input placeholder="type..." formControlName="type" type="text">
<button (click)="addQuestion()">add question</button>
<ng-container formArrayName="answer">
<div [formGroupName]="k" *ngFor="let ans of creatQuestion().get('answer').controls;let k=index;">
<input placeholder="réponse..." formControlName ="answerName" type="text">
<button (click)="addAnswer()">add answer</button>
</div>
</ng-container>
</div>
</ng-container>
</div>
</ng-container>
<button type="submit">submit</button>
</form>
{{questionnaire.value | json}}
First Don't call to creatSection() in the .html
//WRONG
<div [formGroupName]="j" *ngFor="let sec of creatSection().get('question').controls;let j=index;">
//OK
<div [formGroupName]="j" *ngFor="let sec of ques.get('question').controls;let j=index;">
Well you need pass as argument the "indexs" of the arrays (I called "clasic way"), but you can also pass the group of the array itself
<button (click)="addAnswer(i,j)">add answer</button>
//or
<button (click)="addAnswer(sec)">add answer</button>
In a "clasic"
addAnswer(i:number:j:number)
{
const arraySection=((this.questionnaire.get('section') as FormArray)
const groupQuestion=(arraySection.get('question') as FormArray).at(i)
const arrayAnswer=groupQuestion.get('answer') as FormArray
arrayAnswer.push(this.creatAnswer())
}
If you pass the array itself
addAnswer(groupQuestion:FormArray)
{
const arrayAnswer=groupQuestion.get('answer') as FormArray
arrayAnswer.push(this.creatAnswer())
}
NOTE: with a stackblitz was easer check and it's possible eror in my code, please get it the idea
thanks to #Eliseo the result would look like
questionnaire.html
<h1>questionnaire bla bla</h1>
<form [formGroup]="questionnaire" (ngSubmit)="onSubmit()">
<input type="text" placeholder="questionnaire..." formControlName="questionnaireName">
<ng-container formArrayName="section">
<div [formGroupName]="i" *ngFor="let ques of questionnaire.get('section').controls;let i = index;">
<input placeholder="section..." formControlName="sectionName" type="text">
<button (click)="addSection()">add section</button>
<ng-container formArrayName="question">
<div [formGroupName]="j" *ngFor="let sec of ques.get('question').controls;let j=index;">
<input placeholder="question..." formControlName="questionName" type="text">
<input placeholder="type..." formControlName="type" type="text">
<button (click)="addQuestion(ques)">add question</button>
<ng-container formArrayName="answer">
<div [formGroupName]="k" *ngFor="let ans of sec.get('answer').controls;let k=index;">
<input placeholder="réponse..." formControlName ="answerName" type="text">
<button (click)="addAnswer(sec)">add answer</button>
</div>
</ng-container>
</div>
</ng-container>
</div>
</ng-container>
<button type="submit">submit</button>
</form>
{{questionnaire.value | json}}
and questionnaire.ts would look like this
import { Component,OnInit } from '#angular/core';
import { FormBuilder, FormGroup, FormArray } from '#angular/forms'
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
questionnaire:FormGroup;
section:FormArray;
question:FormArray;
answer:FormArray;
constructor(private fb:FormBuilder) { }
ngOnInit() {
this.questionnaire=this.fb.group({
questionnaireName: [''],
section : this.fb.array([this.creatSection()])
})
}
creatSection():FormGroup{
return this.fb.group({
sectionName:[''],
question:this.fb.array([this.creatQuestion()])
})
}
addSection(){
this.section=this.questionnaire.get('section') as FormArray;
this.section.push(this.creatSection());
}
creatQuestion():FormGroup{
return this.fb.group({
questionName:[''],
type:[''],
answer:this.fb.array([this.creatAnswer()])
})
}
addQuestion(groupSection:FormArray){
const arrayQuestion=groupSection.get('question') as FormArray
arrayQuestion.push(this.creatQuestion())
}
creatAnswer():FormGroup{
return this.fb.group({
id:[''],
answerName:['']
})
}
addAnswer(groupQuestion:FormArray){
const arrayAnswer=groupQuestion.get('answer') as FormArray
arrayAnswer.push(this.creatAnswer())
}
onSubmit(){
console.log(this.questionnaire.value);
}
}
I am using angular stepper to display all my data and take input for some of the text boxes, but I want to add my custom validations when user clicks on Next button.
stackblitz - material-stepper-custom-validation
html:
<mat-horizontal-stepper #stepper>
<mat-step>
<div class="m-10">
<input type="text" id="fname" placeholder="First Name" >
</div>
<div class="m-10">
<input type="text" id="lname" placeholder="Last Name" >
</div>
<div>
<button mat-button (click)="checkData()" matStepperNext>Next</button>
</div>
</mat-step>
<mat-step [stepControl]="secondFormGroup" [optional]="isOptional">
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
You are now done.
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-horizontal-stepper>
Here, first name and last name should be validated before go to next stepper, but not using formGroup.
ts:
import {Component, OnInit} from '#angular/core';
import {FormBuilder, FormGroup, Validators} from '#angular/forms';
#Component({
selector: 'stepper-optional-example',
templateUrl: 'stepper-optional-example.html',
styleUrls: ['stepper-optional-example.css']
})
export class StepperOptionalExample implements OnInit {
constructor() {}
ngOnInit() { }
checkData() {
let lname = (<HTMLInputElement>document.getElementById("fname")).value;
if(lname == '') {
alert('error');
}
return false;
}
}
How? - If first name is empty then don't allow them to go Next stepper.
Because you use the template driven approach, you will need to map all input fields into an ngModel somehow. Here is an example for it:
HTML:
<input type="text" required [(ngModel)]="model.name" name="name">
TS:
#Component({
selector: 'stepper-optional-example',
templateUrl: 'stepper-optional-example.html',
styleUrls: ['stepper-optional-example.css']
})
export class StepperOptionalExample implements OnInit {
#ViewChild('stepper') stepper;
model = {
name: 'Initial Value'
}
constructor() {}
ngOnInit() { }
}
Using that you can then, check the attribute onClick. You need to remove the matStepperNext and add the (click) event listener instead like so:
HTML:
<button mat-button (click)="onNext()">Next</button>
TS:
onNext() {
// Validate your value in the function
if (this.model.name !== 'Henry') {
this.stepper.next();
}
}
Other than that I also recommend to take a look on the official guide showing how to implement the template driven approach: https://angular.io/guide/forms
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
}
I am new to angular and trying to implement a reactive form. Below is my html code and TS code
<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="email">Mail</label>
<input type="email" name="email" id="email" formControlName="email" class="form-control">
</div>
<div class="btn btn-primary" type="submit">Sign Up</div>
</form>
Here is my TS file
import { Component , OnInit} from '#angular/core';
import {FormControl, FormGroup, Validators} from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
title = 'app works!';
signupForm : FormGroup;
ngOnInit(){
this.signupForm = new FormGroup({
'email' : new FormControl('test#test.com')
});
// this.signupForm.valueChanges.subscribe(
// (value) => console.log(value)
// );
}
onSubmit(){
console.log(this.signupForm);
}
}
For some reason I cannot print anything on console which I am trying to do in OnSubmit method. I checked everything and it looked okay , but still nothing comes on console when I press the button.
Can anyone please help me what am I doing wrong ?
Attribute type is not a valid type on div element. This this case you will need to have an input or button with type submit for the callback to be triggered.
<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="email">Mail</label>
<input type="email"
name="email"
id="email"
formControlName="email"
class="form-control">
</div>
<button class="btn btn-primary" type="submit">Sign Up</button>
</form>
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