On focus on input how to remove/disabled validation? Angular - javascript

on focus, when I click on one input field I want and if it is not valid to be validation disappears, but when we move to another field the same validation appears. It means only the focus of the current field to take off.
component.ts code
createForm() {
this.contactForm = this.fb.group({
firstName: [null, [Validators.required, Validators.minLength(0),
Validators.maxLength(150)]],
lastName: [null, [Validators.required, Validators.minLength(0),
Validators.maxLength(150)]]
});
}
component.html code
<div class="col-lg-2 col-md-2 col-sm-2 pl-1">
<label class="customer-title" for="account">
First Name<span class="text-danger">*</span>
</label>
<input
name="account"
class="form-control customer-input-field"
placeholder="First Name"
[ngClass]="{'is-invalid': f.firstName.touched && f.firstName.errors }"
formControlName="firstName"/>
<div class="text-danger error-text mt-1"
*ngIf="f.firstName.touched && f.firstName.errors?.required">
First Name is required
</div>
<div
class="text-danger error-text mt-1"
*ngIf="f.firstName.errors?.maxlength">
First name must be less than 150 characters long
</div>
</div>

I dont like the idea of removing validation on focus, because you will potentially allow at least one field to be invalid when a user submits his data.
It is possible with a directive:
import { Directive, ElementRef, HostListener } from "#angular/core";
import { NgControl, ValidatorFn } from "#angular/forms";
#Directive({
selector: "[no-val-on-focus]"
})
export class NoValidationOnFocusDirective {
constructor(
private el: ElementRef<HTMLInputElement>,
private control: NgControl
) {}
private validator: ValidatorFn | null = null;
#HostListener("focus", ["$event"])
public onFocus(): void {
this.validator = this.control.control.validator;
this.control.control.clearValidators();
this.control.control.updateValueAndValidity();
}
#HostListener("blur", ["$event"])
public onBlur(): void {
this.control.control.setValidators(this.validator);
this.control.control.updateValueAndValidity();
}
}
The proceudeure is simple:
On focus we store the validation function in a private member of the directive and clear all validators on the control.
On blur we set the validator of the control to the original function.
Stackblitz example

Related

Form Group getting 'null' or 'undefined' error

i have a component.ts that looks like this :
import { Component, OnInit } from '#angular/core';
import {FormBuilder, FormGroup, Validators} from "#angular/forms";
import {Observable, Subscription} from "rxjs";
import {ArticleService} from "../article.service";
#Component({
selector: 'app-article-new',
templateUrl: './article-new.component.html',
styleUrls: ['./article-new.component.css']
})
export class ArticleNewComponent implements OnInit {
response$?: Subscription;
constructor(private formBuilder: FormBuilder, private articleService: ArticleService) { }
articleForm: FormGroup = this.formBuilder.group({
title: ['', Validators.required],
content: ['', [Validators.required, Validators.minLength(69)]]
})
ngOnInit(): void {
}
async submit(){
console.log('article / sub', this.articleForm.value);
this.response$ = await this.articleService.createArticle(this.articleForm.value).subscribe(
res => console.log(res)
);
}
get title() {
return this.articleForm.get('title');
}
get content() {
return this.articleForm.get('content');
}
}
And the html of the component :
<h3>Create New Article</h3>
<form [formGroup]="articleForm" (ngSubmit)="submit()">
<div class="form-group">
<label for="title">Title :</label>
<input type="text" formControlName="title" class="form-control" id="title" required>
<div *ngIf="title?.invalid" class="alert alert-danger">
Title is required
</div>
</div>
<div class="form-group">
<label for="content">Content :</label>
<textarea class="form-control" formControlName="content" id="content" rows="3" required></textarea>
<div *ngIf="content?.errors && (content?.errors['required'] !== null)" class="alert alert-danger">
Content is required
</div>
<div *ngIf="content?.errors && (content?.errors['minLength'] !== null)" class="alert alert-danger">
Minimum length
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
As you can see, i have 3 divs with *ngIf. The first one with "Title" gave me a similar error that i solved by adding "?" after the variable name.
But with the content variable, adding the question mark didn't change anything. So i have that error for those 2 divs :
TS2533: Object is possibly 'null' or 'undefined'.
Yes, this is strict checking and having "long" paths like (content?.errors['required'] !== null will cause such things. It should clear up by using
content?.errors?.required
but I do prefer using hasError instead, so that is what I would suggest you to use, better readability in my opinion ;) So that would mean simply
content?.hasError('required')
and that can be applied to every formcontrol in your form, any validators. Please not that there is a gotcha with minLenght and maxLength. The L needs to be lowercase, so:
content?.hasError('minlength')
Try to use this :
articleForm= new FormGroup({
title: new FormControl('', Validators.required),
content: new FormControl('', [Validators.required, Validators.minLength(69)])
});
See this documentation :
https://www.concretepage.com/angular-2/angular-2-formgroup-example
Move the form creation logic inside ngOnInit hook
ngOnInit(): void {
this.buildForm();
}
buildForm():void{
this.articleForm = this.formBuilder.group({
title: ['', Validators.required],
content: ['', [Validators.required, Validators.minLength(69)]]
})
}
and add check in html part for articleForm
<form *ngIf="articleForm" [formGroup]="articleForm" (ngSubmit)="submit()">

Strange errors with Angular Reactive Forms Validation

I'm testing a simple login functionality on my products-page component form using Angular 7 and I'm having this strange behaviour. I am trying to display the appropriate validation messages if an error exists, like if it is not a valid email, the " must be a valid email " message should display, and if the field is left blank, the " email is required ", error should display, and when you start typing, the required message disappears and only the valid email error message shows. This is my code.
Addproduct.component.html
So now I'm trying to render the span-messages if the error exists but it's not working.
<form [formGroup]="loginForm" class="login-container" (ngSubmit)="login()">
<p>
<input class="form-control" type="email" placeholder="Email here" formControlName="email">
<span *ngIf="f.email.errors.required">email is required</span>
<span *ngIf="f.email.errors.email">must be a valid email</span>
</p>
<p>
<input class="form-control" type="password" placeholder="Password here" formControlName="password">
<span *ngIf="f.password.errors.required">Password is required</span>
</p>
<p><button type="submit" class="btn btn-md btn-primary">Submit</button></p>
</form>
Addproduct.component.ts
and this is the controller, I tried using the short notation for the validation rules but yet to no avail.
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'app-addproduct',
templateUrl: './addproduct.component.html',
styleUrls: ['./addproduct.component.css']
})
export class AddproductComponent implements OnInit {
loginForm:FormGroup;
isSubmitted:boolean = false;
constructor(
private router:Router,
private fb:FormBuilder
){}
ngOnInit() {
this.loginForm = this.fb.group({
email : ['', [Validators.required,Validators.email]],
password : ['', [Validators.required,Validators.min(6)]]
});
}
get f(){
return this.loginForm.controls;
}
}
I also changed the validation scripts to this but still to no avail
ngOnInit() {
this.loginForm = this.fb.group({
email : new FormControl('', [Validators.required,Validators.email]),
password : new FormControl('', [Validators.required,Validators.min(6)])
});
}
and I am getting this error
You are checking for the presence of an error where no error might exist.
You want something like this:
f.email.errors?.required
Or even:
f.email?.errors?.required
Do the same for the password field and anywhere else where the property might not exist when it is first called.
You can also use
<span *ngIf="loginForm.hasError('required', ['password'])">Password is required</span>
In my opinion it is a lot more easier.

validating and display error messages for specific form controller in a form array angular

import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, FormArray, Validators, ValidatorFn, AbstractControl } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.buildForm();
}
get vehicleGroup(): FormArray {
return <FormArray>this.myForm.get('vehicleGroup');
}
buildForm(): void {
this.myForm = this.fb.group({
name: ['', Validators.required],
vehicleGroup: this.fb.array([
this.fb.group({
vehicle: ['', Validators.required]
})
], [Validators.required]),
});
}
addVehicles(): void{
const itemToAdd = this.fb.group({
vehicle: ['', Validators.required]
});
this.vehicleGroup.push(itemToAdd);
}
deleteVehicle(i: number){
this.vehicleGroup.removeAt(i);
}
save(): void{
console.log('save');
}
}
<form novalidate (ngSubmit)="save()" [formGroup]="myForm">
<div class="form-group">
<label for="name">Name</label>
<input id="name" type="text" formControlName="name">
</div>
<div class="form-group">
<label for="vehicle">Vehicle</label>
<div formArrayName="vehicleGroup" *ngFor="let vehicle of vehicleGroup.controls; let i=index">
<div class="form-group" [formGroupName]="i">
<div>
<input id="{{'vehicle'+i}}" type="text" formControlName="vehicle">
<button type="button" (click)="deleteVehicle(i)"
*ngIf="vehicleGroup.length >= 2">remove
</button>
</div>
</div>
</div>
<div class="form-group">
<button type="button" class="link-button" [disabled]="!vehicleGroup.valid" (click)="addVehicles()">
+ Add more vehicles
</button>
</div>
</div>
</form>
I have this (stackBlitz) simple form created with angular formBuilder
I simple need to how to validate each elements of the dynamic formArray and display a unique message for each of them if that particular field is not valid.
I tried several solutions and also tried custom validator function with return an ValidatorFn. with that I could simply validate the formArray, but It's not good enough for my scenario and still I can not display messages according to the validate functions behavior. How ever to narrow it down, I simply need to know if there is a better way to validate each dynamic elements of this formArray. these are the validate rules.
each filed value should be unique.
need to validate real time
after adding few elements, someone edit previously added field, it also should be validate in real time with every other field values(this is where I got struck, I could validate upwards from the editing field, but not the fields below the editing field is validated accordingly)
If some one can show me some way to achieve this in a right way, It would be really great, since I'm struck with this for almost 3 days now, and still can't get an better solution.
I have used unique validator of #rxweb/reactive-form-validators in my project. It can be used directly in the component without creating any custom validation function.
You can edit your addVehicles method as follows:
addVehicles(): void{
const itemToAdd = this.fb.group({
vehicle: ['', RxwebValidators.unique()]
});
this.vehicleGroup.push(itemToAdd);
}
and adding
ReactiveFormConfig.set({"validationMessage":{"unique":"Enter unique value in the input"}});
to ngOnInit.
Here is the forked stackblitz

Angular reactive forms - manually trigger validation of parent FormGroup when child FormGroup changes

I have an Angular component that defines an FormGroup, which has a nested FormGroup as one of its controls.
The child FormGroup is passed as an #Input parameter to a child component, and there are validators on some of the controls inside the child FormGroup.
For some reason, the valid property of the parent FormGroup only updates once I update values in the child component, then make a random change to one of the inputs for the parent FormGroup (like adding an extra space to an input).
The setup is fairly complex (validators are added or removed from controls on the child FormGroup depending on certain conditions, which is probably why the validation isn't happening automatically).
How can I manually trigger the parent FormGroup to re-validate as soon as anything in the child FormGroup changes? I tried this in the child component:
ngOnInit()
this.myForm.valueChanges.subscribe(val => {
this.myForm.updateValueAndValidity({onlySelf: false, emitEvent: true})
});
This is supposed to trigger a re-validation of the child FormGroup whenever any of its inputs change, and broadcast the event to the parent component, which should trigger a re-validation of the parent FormGroup. I get some kind of stack overflow error though, as this results in an infinite loop.
How can I trigger the parent FormGroup to re-validate automatically?
this.FormGroup.updateValueAndValidity();
updateValueAndValidity() - this is a default method withing FormGroup
i did something like this for example it's my child formgroup:
my child formgroup component:
import { Component, Input } from "#angular/core";
import { FormGroup } from "#angular/forms";
#Component({
selector: 'input-form-control',
templateUrl: './input-form-control.component.html'
})
export class InputFormControlComponent {
public formGroup: FormGroup;
#Input('label') formControlLabel: string;
#Input('controlId') formControlId: string;
#Input('name') formControlName: string;
#Input('type') formControlType: string;
#Input('errorText') formControlErrorText: string;
#Input('helpText') formControlHelpText: string;
#Input('group')
set formControl(group) {
this.formGroup = group;
}
#Input('required') isRequired: boolean;
#Input('value')
set value(value: string) {
this.currentValue = value;
this.store({ value });
this.setAsTouched();
}
}
get value(): string {
return this.currentValue;
}
#Output('valueChange') valueChange: EventEmitter<any> = new EventEmitter();
setAsTouched() {
this.formGroup.controls[this.formControlName].markAsTouched();
}
store(data) {
if (!this.formGroup.controls[this.formControlName]) {
return;
}
if (data.value === EMPTY_ITEM) {
this.formGroup.controls[this.formControlName].setValue('');
this.valueChange.emit({ value: '' });
return;
}
this.formGroup.controls[this.formControlName].setValue(data.value);
this.valueChange.emit(data);
}
}
it's template:
<form [formGroup]="formGroup" class="m-form m-form--fit">
<div class="form-group m-form__group row" [ngClass]="{
'has-danger': formGroup.controls[formControlName].invalid && formGroup.controls[formControlName].touched,
'has-success': formGroup.controls[formControlName].valid && formGroup.controls[formControlName].touched,
'has-no-action': formGroup.controls[formControlName].untouched
}">
<label class="col-form-label col-lg-3 col-sm-12" [for]="formControlId">
{{formControlLabel}}
<span *ngIf="isRequired" class="required" aria-required="true"> * </span>
</label>
<div class="col-lg-4 col-md-9 col-sm-12">
<input [type]="formControlType || 'text'" class="form-control m-input" [formControlName]="formControlName" [name]="formControlName" [id]="formControlId" [placeholder]="formControlLabel" (click)="setAsTouched()" (valueChanged)="store($event)">
<div class="form-control-feedback">{{formControlErrorText || 'Required Field May Not Be Empty'}}</div>
<span class="m-form__help">{{formControlHelpText}}</span>
</div>
</div>
<div class="form-group m-form__group"></div>
</form>
in parent template:
<form [formGroup]="parentFormGroup">
<input-form-control
[required]="false"
[group]="parentFormGroup"
label="Description"
name="description"
controlId="description"
helpText="Enter the Description"
[value]="someValue"
(valueChange)="checkParentValidity($event)">
</input-form-control>
<form>

Validating forms by minlenght, maxlenght, email, number, required etc.. angular 2

I have a sample form in angular
app.html
<form [ngFormModel]="newListingForm" (ngSubmit)="submitListing(newListingForm.value)">
<input type="radio" #typeRadio="ngForm" [ngFormControl]="newListingForm.controls['typeRadio']" value="Event" id="type-event_radio" required>
<input type="radio" #typeRadio="ngForm" [ngFormControl]="newListingForm.controls['typeRadio']" value="Venue" id="type-venue_radio" required>
<input type="text" placeholder="New Title" #mainTitleInput="ngForm" [ngFormControl]="newListingForm.controls['mainTitleInput']" id="main-title_input" class="transparent">
<input type="email" #emailAddressInput="ngForm" [ngFormControl]="newListingForm.controls['emailAddressInput']" id="email-address_input" required>
<!-- etc -->
</form>
app.ts
import {Component} from 'angular2/core';
import {FORM_DIRECTIVES, FormBuilder, ControlGroup, Validators} from 'angular2/common';
#Component({
templateUrl : 'app/components/new-listing/app.html',
directives: [FORM_DIRECTIVES]
})
export class NewListingComponent {
//Helpers
newListingForm: ControlGroup;
//Costructor
constructor(
fb: FormBuilder
){
//New Listing Form
this.newListingForm = fb.group({
'typeRadio': ['', Validators.required],
'mainTitleInput': ['', Validators.required],
'emailAddressInput': ['', Validators.required],
});
}
//TODO Form submission
submitListing(value: string): void {
console.log("Form Submited");
}
}
At the moment only validation I see is default one provided by google on required fields. it all goes away. I've been looking into docs and min/max length seems to be easy to implement with my current setup, however there is another interesting asp NG_VALIDATORS Which I think (not sure) provides validation by looking at things like type="email", required etc.. inside a form. Essentially I want to be able to display messages like invalid email this field is required etc.. through angular2..
Simply use expression like this (sample with Bootstrap) to leverage attributes (valid or not, errors, ...) of the controls you associated with your inputs:
<form [ngFormModel]="newListingForm">
<!-- Field name -->
<div class="form-group form-group-sm"
[ngClass]="{'has-error':!newListingForm.controls.mainTitleInput.valid}">
<label for="for"
class="col-sm-3 control-label">Name:</label>
<div class="col-sm-8">
<input [ngFormControl]="newListingForm.controls.mainTitleInput"
[(ngModel)]="newListing.mainTitleInput"/>
<span *ngIf="!newListingForm.controls.mainTitleInput.valid"
class="help-block text-danger">
<span *ngIf="newListingForm.controls.errors?.required">
The field is required
</span>
</span>
</div>
</div>
</form>
With this sample, fields with errors will be displayed in red and error messages is dynamically displayed.
Moreover you add implement custom validators for your fields:
export class CityValidator {
static validate(control: Control) {
var validValues = [ 'mycity', ... ];
var cnt = validValues
.filter(item => item == control.value)
.length;
return (cnt == 0) ? { city: true } : null;
}
}
Simply add then into your validators for fields:
this.newListingForm = fb.group({
'myfield': ['', Validators.compose([
Validators.required, CityValidator.validate])]
(...)
});
Hope it helps you,
Thierry
#Ilja you are on good track, improve your code with this:
this.newListingForm = fb.group({
'typeRadio': ['', Validators.compose([Validators.required, Validators.minLength(2), Validators.maxLength(40)])],
'mainTitleInput': ['', Validators.compose([Validators.required, Validators.minLength(2), Validators.maxLength(40)])],
'emailAddressInput': ['', Validators.compose([Validators.required, Validators.minLength(2), Validators.maxLength(40)])],
});
Consider this example ( with custom email Validator )
https://plnkr.co/edit/5yO4HviXD7xIgMQQ8WKs?p=info

Categories