Problems with custom form validator in angular - javascript

I create custom form validator for checking equality of password, I mean it checks if the passwords are matching. I can't get a real true or false in my html(variable=signupForm.valid ). Maybe I need to pass on different way controlValuesAreEqual in my form, or there is problem with imports all these forms and validators?
Ts file:
import { HttpClient } from '#angular/common/http';
import { Component, OnInit } from '#angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators, ValidatorFn, AbstractControl, ValidationErrors, FormGroup } from '#angular/forms';
import { Router } from '#angular/router';
#Component({
selector: 'app-signup',
templateUrl: './signup.component.html',
styleUrls: ['./signup.component.scss'],
})
export class SignupComponent implements OnInit {
public signupForm!: UntypedFormGroup;
constructor(
private formBuilder: UntypedFormBuilder,
private http: HttpClient,
private router: Router
) {}
ngOnInit(): void {
this.signupForm = this.formBuilder.group({
email: ['', Validators.required],
name: ['', Validators.required],
password: ['', Validators.required],
confirmPassword: ['',Validators.required],
validators: this.controlValuesAreEqual('password', 'confirmPassword')
});
}
private controlValuesAreEqual(controlNameA: string, controlNameB: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const formGroup = control as FormGroup
const valueOfControlA = formGroup.get(controlNameA)?.value
const valueOfControlB = formGroup.get(controlNameB)?.value
if (valueOfControlA === valueOfControlB) {
return null
} else {
return { valuesDoNotMatch: true }
}
}
}
signUp() {
this.http
.post<any>('http://localhost:3000/signupUsers', this.signupForm.value)
.subscribe({
next: (res) => {
this.signupForm.reset(), this.router.navigate(['login']);
},
error: (e) => alert('Something went wrong'),
});
}
}
And HTML is here
<div class="container mt-5">
<div class="card" style="padding: 20px">
<h2 class="text-center">Please Sign Up!</h2>
<form [formGroup]="signupForm" (ngSubmit)="signUp()">
<mat-form-field appearance="outline" class="w-100">
<mat-label>Email</mat-label>
<input formControlName="email" matInput placeholder="E-Mail" />
<mat-hint>Hint</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline" class="w-100">
<mat-label>Name</mat-label>
<input formControlName="name" matInput placeholder="name" />
<mat-hint>Hint</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline" class="w-100">
<mat-label>Password</mat-label>
<input
type="password"
formControlName="password"
matInput
placeholder="password"
/>
<mat-hint>Hint</mat-hint>
</mat-form-field>
<mat-form-field appearance="outline" class="w-100">
<mat-label>Confirm Password</mat-label>
<input
type="password"
formControlName="confirmPassword"
matInput
placeholder="password"
/>
<mat-hint align="start">
<strong>{{ signupForm.valid }}</strong>
</mat-hint>
</mat-form-field>
<button type="submit" mat-raised-button color="primary" class="w-100">
Sign Up
</button>
</form>
<a style="text-decoration: none; margin-top: 10px" routerLink="/login"
>Already registered? Click to Login</a
>
</div>
</div>

Include this method in your component so that password and confirmed password mismatch error can be emitted to your template.
get passwordMatchError() {
return (
this.signupForm.getError('valuesDoNotMatch') &&
this.signupForm.get('confirmPassword')?.touched
);
}
Place the following code under confirm password mat-form-field section so that the mismatch error can be shown.
<mat-error *ngIf="passwordMatchError">Password and confirmed password do not match.</mat-error>

Related

TypeError: Cannot read property 'get' of null - FormControlName

This is my error
core.js:6479 ERROR TypeError: Cannot read property 'get' of null
at FormGroupDirective.addControl (forms.js:5346)
at FormControlName._setUpControl (forms.js:5929)
at FormControlName.ngOnChanges (forms.js:5874)
at FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.js:1498)
at callHook (core.js:2536)
at callHooks (core.js:2495)
at executeInitAndCheckHooks (core.js:2446)
at selectIndexInternal (core.js:8447)
at Module.ɵɵadvance (core.js:8430)
at SignInComponent_Template (sign-in.component.html:36)
I don't know why and where am I going wrong.
This is my HTML file
<form (formGroup)="signInForm">
<mat-form-field class="full-width">
<mat-label>Email</mat-label>
<input matInput placeholder="Email" name="email" formControlName="email" [(ngModel)]="email">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Password</mat-label>
<input matInput placeholder="Password" name="password" formControlName="pass" [(ngModel)]="pass">
</mat-form-field>
<button
(click)="signIn()"
mat-raised-button
color="primary"
class="login-button">
Sign In
</button>
</form>
<form [formGroup]="signUpForm">
<mat-form-field class="full-width">
<mat-label>Username</mat-label>
<input matInput placeholder="Full name" name="name" formControlName="name" [(ngModel)]="name">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>E-mail</mat-label>
<input matInput placeholder="Email" name="email" formControlName="email" [(ngModel)]="email">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Contact</mat-label>
<input matInput placeholder="Contact" name="contact" formControlName="contact" [(ngModel)]="contact">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Address</mat-label>
<input matInput placeholder="Address" name="address" formControlName="address" [(ngModel)]="address">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Password</mat-label>
<input matInput placeholder="Password" name="pass" formControlName="pass" [(ngModel)]="pass">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Confirm Password</mat-label>
<input matInput placeholder="Confirm Password" name="conpass" formControlName="conpass" [(ngModel)]="conpass">
</mat-form-field>
<!-- <label class="example-margin">Type of User:</label>
<mat-radio-group>
<mat-radio-button class="example-margin" value="after">Buyer</mat-radio-button>
<mat-radio-button class="example-margin" value="before">Seller</mat-radio-button>
</mat-radio-group> -->
<mat-form-field appearance="fill">
<mat-label>Choose one</mat-label>
<mat-select [formControl]="selected" [errorStateMatcher]="matcher" name="option" id="option">
<mat-option value="none">please select</mat-option>
<mat-option value="valid">Buyer</mat-option>
<mat-option value="invalid">Seller</mat-option>
</mat-select>
</mat-form-field>
<button (click)="signUp()"
mat-raised-button
color="primary"
class="login-button">
Sign Up
</button>
</form>
And this is my TS file
import { NumberInput } from '#angular/cdk/coercion';
import { Component, OnInit } from '#angular/core';
import { FormControl, Validators, FormGroupDirective, NgForm, FormGroup, FormBuilder } from '#angular/forms';
import {ErrorStateMatcher} from '#angular/material/core';
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
}
}
#Component({
selector: 'app-sign-in',
templateUrl: './sign-in.component.html',
styleUrls: ['./sign-in.component.css']
})
export class SignInComponent implements OnInit {
email:String="";
name:String="";
contact:NumberInput="";
address:String="";
pass:String="";
conpass:String="";
constructor( private fb : FormBuilder) { }
signUpForm=this.fb.group ({
name : new FormControl(Validators.required),
email : [null,[Validators.email , Validators.required]],
contact :[null,[Validators.required]],
address : [null,[Validators.required]],
pass : [null,[Validators.required]],
conpass : [null,[Validators.required]]
});
signInForm=this.fb.group({
email : [null,[Validators.email , Validators.required]],
pass : [null,[Validators.required]]
});
selected = new FormControl('valid', [
Validators.required,
Validators.pattern('valid'),
]);
selectFormControl = new FormControl('valid', [
Validators.required,
Validators.pattern('valid'),
]);
nativeSelectFormControl = new FormControl('valid', [
Validators.required,
Validators.pattern('valid'),
]);
matcher = new MyErrorStateMatcher();
// favoriteSeason: string;
// seasons: string[] = ['Winter', 'Spring', 'Summer', 'Autumn'];
// onLogin(form : NgForm)
// {
// console.log(form.value)
// }
// onSingup(form : NgForm)
// {
// console.log(form.value)
// }
ngOnInit(): void {
}
signUp(){
if(this.signUpForm.valid || (this.signUpForm.controls.pass.value != this.signUpForm.controls.conpass.value))
{
console.log("Invalid Form!")
return;
}
console.log(JSON.stringify(this.signUpForm.value))
}
signIn(){
if(this.signUpForm.valid)
{
console.log("Invalid Form!")
return;
}
console.log(JSON.stringify(this.signUpForm.value))
}
}
I thought that not mentioning ngModel would be an issue first but even after adding that the same error again and again, when I run the code it my GUI is totally destroyed and disrupted showing the above error.
Please help me with this, stuck since forever here.
Your signInForm form group directive should be in a square bracket.
(formGroup)="signInForm"
It should be like,
[formGroup]="signInForm"

Angular: Not able to reuse the same component for multiple forms

I am trying to reuse an existing component in a single page where I am required to display multiple forms on the same page based on the no of users. For example if I have 10 users then I should have 10 address forms and map them to the appropriate user. I tried many ways but not able to get there. I understand that I may have to use a formarray to achieve this I just don't know how.
Here's my code:
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted))
}
}
#Component({
selector: 'rms-address',
templateUrl: './address.component.html',
styleUrls: ['./address.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AddressComponent),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AddressComponent),
multi: true,
},
],
})
export class AddressComponent implements OnInit, ControlValueAccessor, OnDestroy {
name: String
abbreviation: string
#Input() address: Address
#Output() newItemEvent = new EventEmitter<Address>();
selectedClass: any
subscriptions: Subscription[] = []
addressForm = this.fb.group({
addressLine1: ['', Validators.required],
addressLine2: [''],
city: ['', Validators.required],
state: ['', Validators.required],
zipCode: ['', [Validators.required, Validators.pattern('^[ 0-9]*$')]],
})
get value(): Address {
return this.addressForm.value
}
set value(value: Address) {
this.addressForm.setValue(value)
this.onChange(value)
this.onTouched()
}
matcher = new MyErrorStateMatcher()
constructor(private fb: FormBuilder) {
this.selectedClass = Constants
this.subscriptions.push(
this.addressForm.valueChanges.subscribe((value) => {
this.onChange(value)
this.onTouched()
})
)
}
ngOnInit() {
this.newItemEvent.emit();
}
ngOnDestroy() {
this.subscriptions.forEach((s) => s.unsubscribe())
}
onChange: any = () => { }
onTouched: any = () => { }
registerOnChange(fn) {
this.onChange = fn
}
writeValue(value) {
if (value) {
this.value = value
}
if (value === null) {
this.addressForm.reset()
}
}
registerOnTouched(fn) {
this.onTouched = fn
}
validate(_: FormControl) {
return this.addressForm.valid ? null : { address: { valid: false } }
}
}
address.component.html
<form [formGroup]="addressForm" class="address-form">
<mat-form-field class="full-width">
<mat-label>Address 1</mat-label>
<input type="text" matInput formControlName="addressLine1" required [errorStateMatcher]="matcher" />
<mat-error *ngIf="addressForm.hasError('addressLine1') && !addressForm.hasError('required')">
Please enter a valid address
</mat-error>
<mat-error *ngIf="addressForm.get('addressLine1').hasError('required')">
Address 1 is required
</mat-error>
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Address 2</mat-label>
<input type="text" matInput formControlName="addressLine2" [errorStateMatcher]="matcher" />
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>City</mat-label>
<input type="text" matInput formControlName="city" required [errorStateMatcher]="matcher" />
<mat-error *ngIf="addressForm.get('city').hasError('required')">
City is required
</mat-error>
</mat-form-field>
<table class="full-width" cellspacing="0">
<tr>
<td>
<mat-form-field appearance="fill">
<mat-label>State</mat-label>
<mat-select formControlName="state" required>
<mat-option *ngFor="let state of selectedClass.USStates" [(value)]="state.key">
{{ state.key }}
</mat-option>
</mat-select>
<mat-error *ngIf="addressForm.get('state').hasError('required')">Please choose a valid state </mat-error>
</mat-form-field>
</td>
<td>
<mat-form-field class="full-width">
<mat-label>ZIP</mat-label>
<input type="text" matInput formControlName="zipCode" required maxlength="5" [errorStateMatcher]="matcher" />
<mat-error *ngIf="addressForm.get('zipCode').hasError('required')">
Zipcode is required
</mat-error>
<mat-error
*ngIf="addressForm.get('zipCode').hasError('pattern') && !addressForm.get('zipCode').hasError('required')">
Please enter a valid zipcode
</mat-error>
</mat-form-field>
</td>
</tr>
</table>
</form>
user-address.component.html:
<form [formGroup]="userAddressForm">
<div *ngIf="!isSameAddress">
<ul class="list-group padding renewal-list">
<li class="list-group-item" *ngFor="let user of users">
<div class="card address-card">
<div class="card-body">
<rms-address formControlName="registeredAddress">
</rms-address>
</div>
</div>
</div>
<hr class="horizontal-line" />
</li>
</ul>
</div>
<div *ngIf="isSameAddress">
<rms-address formControlName="sameRegisteredAddress"></rms-address>
</div>
</form>
user-address.component.ts:
export class UserAddressComponent implements OnInit {
users: Users[]
isSameAddress: boolean = false
#Input() data: any
changeOfAddressForm = this.fb.group({
registeredAddress: this.fb.array([]),
sameRegisteredAddress: [],
isSameAddress: [false]
})
}

Updating <mat-icon> from one <mat-form-field> to another doesn't consistently update the icon

I have been trying to make a form for blog posting, and I want to have selectable category icons next to the title.
I made a form with selectable font awesome icons, but when I select a category I can't select another one to change the icon again. It only changes if I "reset" it with the "None" option in-between each selection.
However, when I change the code to use the icon name as a string, instead of as an icon input it changes with every selection, as I want it to. I just don't understand how it can update the text regularly but not the icon.
This is my actual code:
[create.component.html]
...
<div class="blog-container">
<mat-card class="blog-card">
<section>
<h2>Create blog post</h2>
</section>
<form [formGroup]="createForm" class="width-1">
<div class="width-1">
<mat-form-field class="blog-title" appearance="outline">
<mat-label>Post Title*</mat-label>
<input matInput formControlName="newTitle" #newTitle>
<mat-icon matSuffix *ngIf="category" [fontSet]="category.set" [fontIcon]="category.icon"></mat-icon>
<mat-hint>the first two fields are required</mat-hint>
</mat-form-field>
</div>
<div class="width-1">
<mat-form-field class="blog-text" appearance="outline">
<mat-label>Bread Text*</mat-label>
<textarea matInput rows="2" formControlName="newText" #newText></textarea>
</mat-form-field>
</div>
<div class="width-1 blog-row">
<mat-form-field class="blog-author" appearance="fill">
<mat-label>Author</mat-label>
<input matInput formControlName="newAuthor" #newAuthor>
</mat-form-field>
<mat-form-field class="blog-category" appearance="fill">
<mat-label>Category</mat-label>
<mat-select [(value)]="category" formControlName="newCategory" #newCategory>
<mat-select-trigger *ngIf="category">
<span>{{category.name}}</span>
</mat-select-trigger>
<mat-option [value]="null">
<span>None</span>
</mat-option>
<mat-option *ngFor="let category of categories" [value]="category">
<mat-icon [fontSet]="category.set" [fontIcon]="category.icon"></mat-icon>
<span>{{category.name}}</span>
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="width-1 center">
<button mat-raised-button color="primary" routerLink="/archive">BACK</button>
<button type="submit" (click)="addPost(title.value, text.value, author.value, category.value)" [disabled]="createForm.pristine || createForm.invalid" mat-raised-button color="primary">POST</button>
</div>
</form>
</mat-card>
</div>
...
[create.component.ts]
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { PostService } from '../../post.service';
#Component({
selector: 'app-create',
templateUrl: './create.component.html',
styleUrls: ['./create.component.scss']
})
export class CreateComponent implements OnInit {
createForm: FormGroup;
category: CategoryDTO = null;
categories: CategoryDTO[] = [
new CategoryDTO({name: 'Programming', set: 'fas', icon: 'fa-laptop-code'}),
new CategoryDTO({name: 'Croshetting', set: 'fas', icon: 'fa-cut'}),
new CategoryDTO({name: 'Arts/Crafts', set: 'fas', icon: 'fa-tools'})
];
constructor(private postService: PostService, private fb: FormBuilder, private router: Router) {
this.createForm = this.fb.group({
newTitle: ['', Validators.required],
newText: ['', Validators.required],
newAuthor: '',
newCategory: ''
});
}
addPost(newTitle, newText, newAuthor, newCategory){
this.postService.addPost(newTitle, newText, newAuthor, newCategory).subscribe(() => {
this.router.navigate(['/archive']);
});
}
ngOnInit() {
}
}
class CategoryDTO {
name: string;
set: string;
icon: string;
constructor(category?: any) {
this.name = category && category.name || null;
this.set = category && category.set || null;
this.icon = category && category.icon || null;
}
}
Since I can't load the Font Awesome CSS in StackBlitz (as far as I know), this is the closest I could come to reproducing a manageable code there.
I am new to Angular.
I imported FormsModule and CommonModule (FontAwesomeModule was already there) in my create.component.ts file, and I re-added node_modules/#fortawesome/fontawesome-free/css/all.min.css in "styles: [...]" in angular.json, and that did the trick!
As far as I can tell, having import { faCut, faCode, faTools } from '#fortawesome/free-solid-svg-icons'; in create.component.ts makes no difference.
Thanks to "Allabakash" for pointing me in the right direction. :)

Form control problem with angular Material

I have this message appear in my console when I want edit my user.
I see some solution on the web, Some people have trouble with it but no solution for me.
Does someone have the same problem ?
EditUserDialogComponent.html:37 ERROR Error:
ngModel cannot be used to register form controls with a parent formGroup directive. Try using
formGroup's partner directive "formControlName" instead. Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>
In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});
Or, if you'd like to avoid registering this form control, indicate that it's standalone in ngModelOptions:
Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
<input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
</div>
at Function.modelParentException (forms.js:6182)
at NgModel._checkParentType (forms.js:6754)
at NgModel._checkForErrors (forms.js:6740)
at NgModel.ngOnChanges (forms.js:6640)
at checkAndUpdateDirectiveInline (core.js:31905)
at checkAndUpdateNodeInline (core.js:44366)
at checkAndUpdateNode (core.js:44305)
at debugCheckAndUpdateNode (core.js:45327)
at debugCheckDirectivesFn (core.js:45270)
at Object.eval [as updateDirectives] (EditUserDialogComponent.html:42)
and this is my HTML,
I think it's a problem about the form control, but if I follow the instructions of the error message,
I have the same...
<div class="manage-content">
<div class="title">
<mat-icon class="user-icon">how_to_reg</mat-icon>
<h3>Edit a user</h3>
</div>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field class="full-width-input">
<input id="firstName" matInput placeholder="First name" formControlName="f_name" [(ngModel)]="selectedUser.f_name"#f_name>
<mat-error *ngIf="isFieldInvalid('f_name')">
The first name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="middleName" matInput placeholder="Middle name" formControlName="m_name" [(ngModel)]="selectedUser.m_name" #m_name>
<mat-error *ngIf="isFieldInvalid('m_name')">
The middle name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="lastName" matInput placeholder="Last name" formControlName="l_name" [(ngModel)]="selectedUser.l_name" #l_name>
<mat-error *ngIf="isFieldInvalid('l_name')">
The last name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="email" matInput placeholder="Email" formControlName="email" [(ngModel)]="selectedUser.email" #email>
<mat-error *ngIf="isFieldInvalid('email')">
The email you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<div class="visibility">
<input id="password" matInput type="password" placeholder="Password" formControlName="password" [(ngModel)]="selectedUser.password">
<mat-icon *ngIf="isPasswordVisible" (click)=showPassword()>visibility</mat-icon>
<mat-icon *ngIf="!isPasswordVisible" (click)="showPassword()">visibility_off</mat-icon>
</div>
<mat-error *ngIf="isFieldInvalid('password')">
The password you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Role" class="full-width-input" [(ngModel)]="selectedUser.role">
<mat-option value="option">End-User</mat-option>
<mat-option value="option">View-User</mat-option>
<mat-option value="option">Vendor</mat-option>
<mat-option value="option">Tool Guy</mat-option>
</mat-select>
</mat-form-field>
<div class="cta-btn">
<button mat-raised-button class="createUserBtn" color="primary" type="submit">Update user</button>
<button mat-raised-button class="createUserBtn" color="warn" type="submit" (click)="click()">Cancel</button>
</div>
</form>
</div>
#Component({
selector: 'app-edit-user-dialog',
templateUrl: 'edit-user-dialog.component.html',
styleUrls: ['./manage-user.component.scss']
})
export class EditUserDialogComponent {
form: FormGroup;
formSubmitAttempt: boolean;
isPasswordVisible = false;
selectedUser: User;
constructor(private formBuilder: FormBuilder, private userService: UserService, public dialog: MatDialog, public dialogRef: MatDialogRef<EditUserDialogComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) {
this.selectedUser = data;
console.log(this.selectedUser);
this.form = this.formBuilder.group({
email: ['', Validators.compose([Validators.required, Validators.pattern('^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$')])],
password: ['', Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(35)])],
f_name: ['', Validators.compose([Validators.required, Validators.maxLength(100)])],
m_name: ['', Validators.compose([Validators.maxLength(100), Validators.pattern('[0-9]+')])],
l_name: ['', Validators.compose([Validators.required, Validators.maxLength(100)])]
// client: ['', Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(35)])],
});
}
click(): void {
this.dialogRef.close();
}
showPassword() {
const pass: HTMLInputElement = document.getElementById('password') as HTMLInputElement;
if (pass.type === "password") {
this.isPasswordVisible = true;
pass.type = "text";
} else {
this.isPasswordVisible = false;
pass.type = "password";
}
}
isFieldInvalid(field: string) {
return (
(!this.form.get(field).valid && this.form.get(field).touched) ||
(this.form.get(field).untouched && this.formSubmitAttempt)
);
}
onSubmit() {
// if (this.form.valid) {
// this.authService.login(this.form.value);
// }
this.formSubmitAttempt = true;
}
}
thanks a lot for your help
The issue you are having is the usage of the ([ngModel]) inside of a formGroup.
If you need to extract a value, you will have to do it via the formGroup variable on the TS component.
Check out more information on the Angular support page: https://angular.io/guide/reactive-forms
change this:
`<input id="password" matInput type="password" placeholder="Password" formControlName="password" [(ngModel)]="selectedUser.password">`
into this:
`<input id="password" matInput type="password" placeholder="Password" formControlName="password" (change)="selectedUserChanged($event)">`
//and add to your ts
`selectedUserChanged(change: any) {
this.group.get('password').setValue(change.value);
}`

Form Validation between multiple input fields

So I have a form with multiple input form fields,and a nested form group. In the nested formgroup, the validation should be such that if any one of the three inputs is filled, then the form should be valid. The person can fill either all, or even just one and the form should be valid. My template code is as follows:
<h3 class="form-title">{{title}}</h3>
<form [formGroup]="formX" novalidate>
<div formGroupName="brandIdentifier">
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="brandName.valid && brandName.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="brandName.invalid && brandName.touched">close</mat-icon>
<input matInput type="text" placeholder="Brand Name" formControlName="brandName" />
</mat-form-field>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="brandId.valid && brandId.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="brandId.invalid && brandId.touched">close</mat-icon>
<input matInput type="text" placeholder="Brand Id" formControlName="brandId" />
</mat-form-field>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="contentId.valid && contentId.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="contentId.invalid && contentId.touched">close</mat-icon>
<input matInput type="text" placeholder="Content Id" formControlName="contentId" />
</mat-form-field>
</div>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="startTime.valid && startTime.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="startTime.invalid && startTime.touched">close</mat-icon>
<input matInput type="text" placeholder="Start Time" formControlName="startTime" />
</mat-form-field>
<mat-form-field class="full-width">
<mat-icon matSuffix color="primary" *ngIf="endTime.valid && endTime.touched">done</mat-icon>
<mat-icon matSuffix color="primary" *ngIf="endTime.invalid && endTime.touched">close</mat-icon>
<input matInput type="text" placeholder="End Time" formControlName="endTime" />
</mat-form-field>
<button mat-raised-button color="primary" (click)="startAnalysis()" [ngClass]="{'form-valid':formX.valid, 'form-invalid':formX.invalid}">Submit</button>
<pre>{{formX.value | json}}</pre>
</form>
How would I go about this? using Validator class is a given, but I'm unable to get it optional.
You can use a custom validator for this, apply the custom validator for the nested group, where we check that at least one of the three fields has a value else than empty string, so here's a sample:
Build form:
this.myForm = this.fb.group({
nestedGroup: this.fb.group({
first: [''],
second: [''],
third: ['']
// apply custom validator for the nested group only
}, {validator: this.myCustomValidator})
});
Custom validator:
myCustomValidator(group: FormGroup) {
// if true, all fields are empty
let bool = Object.keys(group.value).every(x => group.value[x] === '')
if(bool) {
// return validation error
return { allEmpty: true}
}
// valid
return null;
}
Then in your form you can show the validation message by:
<small *ngIf="myForm.hasError('allEmpty','nestedGroup')">
Please fill at least one field
</small>
Finally a DEMO :)
Please follow below steps:
Note: we are just giving idea how you can solve your problem.
import FormsModule, ReactiveFormsModule in module.ts
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [FormsModule, ReactiveFormsModule]})
Modify your html as given e.g.
User name
User name is required.
User name must be at least 4 characters long.
Password
Password is required.
Password must be at least 6 characters long.
Forget the password ?
<div class="form-group">
<button type="submit" (click)='onSubmit()' dir-btn-click [disabled]="!loginForm.valid" class="btn btn-inverse btn-block">Sign in</button>
</div>
</form>
Modify code as below in [yourcomponent].ts e.g.:
import { Component } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
loginForm: FormGroup;
constructor( private fb: FormBuilder) {
this.crateLoginForm()
}
get userName() { return this.loginForm.get('userName'); }
get password() { return this.loginForm.get('password'); }
crateLoginForm() {
this.loginForm = this.fb.group({
userName: ['', [Validators.required, Validators.minLength(4)]],
password: ['', [Validators.required, Validators.minLength(6)]]
})
}
onSubmit() {
let userName= this.loginForm.value.userName;
let pwd =this.loginForm.value.password;
}
}

Categories