Angular 7 template driven forms dynamic validation - javascript

I am wondering how can I dynamically validate a form input that loops through an array using angular 7. For example if I had a first name and last name field it would have a min characters and a required validator, in the same array I could have an age input that would have a min-max and required validators, I was thinking maybe I should put them into a switch case and send the type to a validate function but I'm not too sure how I could do that
This is the code I have to loop through the array I store the type and name of the input
/*Function to get the form*/
getForm() {
this.api.getForm(this.Param).subscribe(
data => {
this.form = data;
console.log(this.form);
}
);
}
<div class="contents">
<form name="form" (ngSubmit)="onSubmit()" #f="ngForm" novalidate>
<div class="row">
<div *ngFor="let questions of form?.results[0].fields">
<div class="md-form">
<div class="col-12 p-3 ml-2">
<span class="">{{questions.name}}</span><br />
<input mdbInput type="{{questions.type}}" />
</div>
</div>
</div>
</div>
</form>
</div>
I was also looking at reactive forms but I don't think it would work for what I need. Thank you in advance

Related

Get/subtract current value from an input tag Angular/Typescript

I am trying to dynamically update a sum of two fields in angular based on user input. At first it is linked to some data taken from the database. I managed to change the input and update the sum, but I somehow need to subtract what was before in the field in sum calculation. Now the sum adds also what was previously in the field and I don't want that behaviour.
.html file
<div class="d-flex justify-content-center">
<form>
<input class = "textbox" type="text" name="Name" [ngModel]="user._30_geplant (change)="updateSum($any($event.target).value)">
</form>
</div>
<div class="d-flex justify-content-center">
<form>
<input class = "textbox" type="text" name="Name" [ngModel]="user._30_buro (change)="updateSum($any($event.target).value)">
</form>
</div>
<div class="d-flex justify-content-center">
<p class = "delta" [ngModel]="delta30" ngDefaultControl>{{delta30}}</p>
</div>
In the .ts file
constructor() {
this.delta30 = this.user._30_geplant + this.user._30_buro;
}
updateSum(sum: number) {
this.delta30 = (+this.delta30) + (+sum);
}
What I have tried so far seemed not to work.

How to validate Angular fromgroup input based on the value of a 2nd input in the form?

I have a [formGroup] in Angular version8 app.
I am trying to validate the form based on these simple rules:
if a file is uploaded (includes just choosing any file with the button) then the Reason input is NOT required, else
if a file has not been chosen (ie 'choose file' has no value) then a Reason must be provided for form validation to pass.
File upload is optional if a Reason text is provided.
How do I check inside the Reason textArea that a file has been chosen? I simply can't seem to write a validation statement using that ngClass
template:
<form [formGroup]="dialogForm">
<div class="row">
<div class="lbl-col">
<label for="reason">Reason<span class="required-star">*</span></label>
</div>
<div class="col">
<textarea
id="reasonForChange"
formControlName="reasonForChange"
name="reasonForChange"
type="text"
[(ngModel)]="selectedReasonText"
[ngClass]="{invalid: dialogForm.get('File').invalid}"
>
</textarea>
</div>
</div>
<div class="row">
<div class="lbl-col"></div>
<div class="col">
<input
type="file"
id="reasonFile"
formControlName="File"
(change)="onFileChange($event)"
/>
</div>
</div>
</form>
[...]
// submit button
<button
class="primary-button"
label="update status"
(click)="update()" // a POST nothing more.
[disabled]="dialogForm.invalid"
></button>
and in my controller:
constructor(
private fb: FormBuilder
) {
this.dialogForm = this.fb.group({
chooseStatus: [null, Validators.required],
reasonForChange: [null, Validators.required],
File: [null],
});
Using [ngClass]="{invalid: dialogForm.get('File').invalid}" or variations of it, it doesn't appear to check formControlName="File" but instead checks itself, 'reasonforChange'.
I have also tried the crossValidation template from Ang docs.
Extend the Formbuilder:
constructor(
private fb: FormBuilder
) {
this.dialogForm = this.fb.group({
chooseStatus: [null],
reasonForChange: [null],
File: [null],
}, {validators: crossFormValidator}
);
and provide
export const crossFormValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const reasonForChange = control.get('reasonForChange');
const file= control.get('File');
return file && reasonForChange && attachment.value !== '' ? { crossValid: true } : null;
};
but that errors on this line in my template
<div *ngIf="dialogForm.errors?.['crossValid'] && (dialogForm.dirty)" class="form-validation-warning">
<span class="warning-label">* Required</span>
</div>
at Parser Error: Unexpected token [, expected identifier or keyword at column 20 in [dialogForm.errors?.['crossValid'] && (dialogForm.dirty)]
Even though that is taken almost verbatiim from the angular docs at https://angular.io/guide/form-validation#adding-cross-validation-to-template-driven-forms
Well, there is a simple approach for that, to validate different fields in angular.
Create a getter function in .ts file as follow:
get If() {
return this.dialogForm.controls;
}
This function is returning the controls of each field in the form.
Now, apply this function on the form fields as follow:
<form [formGroup]="dialogForm">
<div class="row">
<div class="lbl-col">
<label for="reason">Reason<span class="required-star">*</span></label>
</div>
<div class="col" *ngIf="If.File.valid || !If.File.untouched">
<textarea
id="reasonForChange"
formControlName="reasonForChange"
name="reasonForChange"
type="text"
[(ngModel)]="selectedReasonText"
[ngClass]="{invalid: dialogForm.get('File').invalid}"
>
</textarea>
</div>
</div>
<div class="row">
<div class="lbl-col"></div>
<div class="col">
<input
type="file"
id="reasonFile"
formControlName="File"
(change)="onFileChange($event)"
/>
</div>
</div>
// submit button
<button
class="primary-button"
label="update status"
(click)="update()" // a POST nothing more.
[disabled]="dialogForm.invalid"
></button>
</form>
This way, it is going to work as expected

Angular 5 reactive forms validators error

I have been stuck at this silly angular 5 reactive forms error which i am not able to get rid of. While adding the validation message block in html, I am getting the error
"Cannot read property 'invalid' of undefined"
which is weird as there is a form control element with the same name and I am able to access the value of that feild. Below is the code
HTML file
<form [formGroup]='signUpForm'>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" formControlName='password'>
<div *ngIf="password.invalid && (password.dirty || password.touched)"
class="alert alert-danger">
<div *ngIf="password.errors.required">
Name is required.
</div>
</div>
</div>
</div>
</div>
</form>
ts code
signUpForm: FormGroup;
this.signUpForm = new FormGroup({
username:new FormControl('',Validators.required),
email:new FormControl('',[Validators.required]),
password:new FormControl('',Validators.required),
})
Please help. Thanks in advance
Try This Code.
<div *ngIf="signUpForm.controls['password'].invalid && (signUpForm.controls['password'].dirty || signUpForm.controls['password'].touched)"
class="alert alert-danger">
<div *ngIf="signUpForm.get('password').hasError('required')">
Name is required.
</div>
</div>
Where do you initialize the form?? If you do it in ngOnInit(), you should not be facing that problem.
If you are initializing the form under any other custom method of yours then you can use something like:
Use the safe navigation operator ?. This checks whether the first operator results to true or not. When the view is rendered it might be possible that the control is not initialized yet. So suppose if the component variable password is not defined when your view is rendered and you try to access a property invalid of password(which is undefined), you will get the error you get now..
for example:
<div *ngIf="password?.invalid && (password?.dirty || password?.touched)" class="alert alert-danger">
So your actual view could be like:
<form [formGroup]='signUpForm'>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" formControlName='password'>
<div *ngIf="password?.invalid && (password?.dirty || password?.touched)" class="alert alert-danger">
<div *ngIf="password?.errors?.required">
Name is required.
</div>
</div>
</div>
</div>
</div>

jQuery validate single input in similar forms

I have two forms on a page that are identical, but I'm trying to validate the one field (which is email in this case), but I can't seem to get it to just validate the one input field as it just shows the error for both forms.
HTML:
<div class="general-form">
<div class="email-error" style="display:none;">
<p>You need valid email</p>
</div>
<div class="form-wrap">
<div class="form-row">
<input id="from-email" type="email" name="email" placeholder="Your Email" />
</div>
<div class="btn-row">
<button class="submit-btn">Submit</button>
</div>
</div>
</div>
<div class="general-form">
<div class="email-error" style="display:none;">
<p>You need valid email</p>
</div>
<div class="form-wrap">
<div class="form-row">
<input id="from-email" type="email" name="email" placeholder="Your Email" />
</div>
<div class="btn-row">
<button class="submit-btn">Submit</button>
</div>
</div>
</div>
JS:
$(".submit-btn").on("click", function() {
var $this = $(this);
var valid_email = $this.find("#from-email").val();
if (/(.+)#(.+){2,}\.(.+){2,}/.test(valid_email)) {
return true;
} else {
$this.parents().find(".email-error").show();
return false;
}
});
Overall, I can get it to pass through the validation, but the error message shows for both forms and I'm not sure how to get it so it only shows the error message for that particular form. I'm guessing that I'm pushing too far up the chain and it's testing for both of the forms, but I can't remember which one to target specifically if that makes any sense.
You doubled the id from-email that’s why. In your JS you are checking all fields with the id from-email in this case both of the inputs are checked because both the id.
If one of them is wrong you are searching for the email-error in all of your parents which will go up to the body and then find all off the error wrappers. $this.parents(“.general-form“) will do the deal and only go up to the wrapper of the input and error in your case.
Always make sure your id’s are unique.
Just add required> attribute and add this js
$("#formid").validate();

Unable to assign form control to template variable in Reactive Forms - angular?

I am new to angular let say, i have reactive form like follow
ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
email: [ '', Validators.compose([Validators.required, Validators.email])],
password: this.formBuilder.group({
first: [ '', Validators.required ],
second: [ '', Validators.required, ]
})
});
}
and my angular template looks like follow
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email"
class="form-control">
</div>
<div *ngIf="!registerFormGroup.get('password').get('first').valid"
class="alert alert-danger">
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-success btn-lg"
[disabled]="!registerFormGroup.valid">Submit</button>
</div>
For example, email field has two validations such as required and email type validate so depends upon the error I have to display error message so in my template am using like
<div *ngIf="!registerFormGroup.get('email').valid && (registerFormGroup.get('email').touched)"
class="alert alert-danger">
</div>
Instead of adding same registerFormGroup.get('email') again and again i trying to create template expression like #emailForm="registerFormGroup.get('email')" in
<input type="email" formControlName="email" placeholder="Enter Email" class="form-control" #emailForm="registerFormGroup.get('email')">
so that i can use use like
<div *ngIf="!emailForm.valid" class="alert alert-danger">
</div>
but i am getting error like
compiler.es5.js:1690 Uncaught Error: Template parse errors:
There is no directive with "exportAs" set to "registerFormGroup.get('email')" ("l>
]#emailForm="registerFormGroup.get('email')">
what mistake i made??
You can create common function to access form like below:
validateFormControl(controName: string) {
let control = registerFormGroup.get(controName);
return control.invalid && control.touched;
}
In Templete use this call wherever you need, you just need to pass your control name in function, you can modify this function as per your need as well and you do not need to use form.get all the time. This makes your template more cleaner and performance efficient.
<div *ngIf="validateFormControl('email')"
class="alert alert-danger">
error message
</div>
<div *ngIf="validateFormControl('password')"
class="alert alert-danger">
error message
</div>
Create a method on the component that returns if the form is valid or not and return it in the component
checkError(){ // either you can pass the form from the template or use the component for decleration
return registerFormGroup.get('email').valid;
}
In the template call
<div *ngIf="checkError()" class="alert alert-danger">
// Always make sure not to use controls in the template it will result in AOT compilation error
</div>
try this :
component.html
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" [ngClass]="{'has-error':!registerFormGroup.controls['email'].valid}">
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email"
class="form-control">
<p class="alert alert-danger" *ngIf="registerFormGroup.controls['email'].dirty && !registerFormGroup.controls['email'].valid">Invalid email address</p>
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-primary" [disabled]="!registerFormGroup.valid">Submit</button>
</div>
</form>
</div>
component.ts
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
export class AppComponent implements OnInit {
registerFormGroup: any;
constructor(
private formBuilder: FormBuilder
) {}
ngOnInit() {
this.registerFormGroup = this.formBuilder.group({
email: [null , Validators.compose([Validators.required, Validators.email])]
});
}
}
Angular team strongly discourage from using functions outputs in templates due to change detection strategy. You might be interested in following solution, using ngForm directive:
<div class="container">
<form [formGroup]="registerFormGroup"
(ngFormSubmit)="registerUser(registerFormGroup.value)" novalidate>
<div class="form-group" >
<label for="email">Email</label>
<input type="email" formControlName="email" placeholder="Enter Email "
class="form-control" #email="ngForm">
</div>
<div *ngIf="email.invalid"
class="alert alert-danger">
</div>
It's still a hush to copy-paste it in template, but at least you might have direct reference to control via it's template variable. I personally go with controller getter function still, but I'm posting this answer for sake of answer completeness.
Although I agree with the accepted answer (you should have a dedicated method in your form component that will encapsulate the validation process)
sometimes you need a quick check in the template:
The trick is to expose the formGroup from your component and use it like so:
Template:
<input id="name" class="form-control"
formControlName="name" required
[class.is-invalid]="
f.name.invalid &&
(f.name.touched ||
f.name.touched.dirty)">
Component:
//easily access your form fields
get f() { return this.checkoutForm.controls; }
I was also looking for a more elegant solution than calling form.get multiple times. Here is what I come up with using ngif
<div class="col-sm-6" *ngIf="form.get('sponsor') as sponsor">
<input type="text" formControlName="sponsor" id="sponsor" class="form-control" name="sponsor"
[class.is-invalid]="sponsor.errors && sponsor.touched">
</div>
using the as feature on ngIf to create a template variable

Categories

Resources