Reputation: 404
I have an Angular reactive form with three checkboxes:
My issue is that, even if all these requirements are met, the submission form is still invalid. I would like to see that my submissionForm.valid
returns true
. Here is my submission form component:
EDIT: thanks to the answer in the comments, I see that all my fields are returning null
values. Could anyone please help me fix this as well? I feel that I am initializing everything correctly but I am still confused as to why the values are returning as null
.
import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-submission',
templateUrl: './submission-form.component.html',
styleUrls: [
'./submission-form.component.scss'
]
})
export class SubmissionFormComponent implements OnInit {
submissionForm: FormGroup;
formSubmitted = false; //Holds status of the form
private nameValidators = [
Validators.minLength(1),
Validators.maxLength(50)
];
private emailValidators = [
Validators.maxLength(250),
Validators.email
];
constructor(private fb: FormBuilder) { }
ngOnInit(): void {
this.createForm();
}
createForm(): void {
this.submissionForm = this.fb.group({
anonymous: [''],
name: ['', this.nameValidators],
contact: [''],
email: ['', this.emailValidators],
contentWarning: ['', [Validators.required]],
title: ['', Validators.required],
message: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10000)]],
agree: [false, [Validators.requiredTrue]]
});
}
onSubmit() {
if (this.submissionForm.get('anonymous').value == false) {
this.submissionForm.get('name').setValidators(this.nameValidators.concat(Validators.required));
} else {
this.submissionForm.get('name').setValidators(this.nameValidators);
}
if (this.submissionForm.get('contact').value == true) {
this.submissionForm.get('email').setValidators(this.emailValidators.concat(Validators.required));
} else {
this.submissionForm.get('email').setValidators(this.emailValidators);
}
this.formSubmitted = true; //Form has been submitted
console.log(this.submissionForm.valid);
if (this.submissionForm.valid) {
console.log('submissionForm', this.submissionForm.value); //Process the form
}
}
get anonymous() { return this.submissionForm.get('anonymous') };
get name() { return this.submissionForm.get('name') };
get contact() { return this.submissionForm.get('contact') };
get email() { return this.submissionForm.get('email') };
get contentWarning() { return this.submissionForm.get('contentWarning') };
get title() { return this.submissionForm.get('title') };
get message() { return this.submissionForm.get('message') };
get agree() { return this.submissionForm.get('agree') };
}
Here is the accompanying template code:
<section class="section">
<div class="columns is-centered">
<div class="column is-four-fifths">
<h1 class="title">Submission Form</h1>
<p class="success" *ngIf="formSubmitted">Submitted successfully</p>
<form [formGroup]="submissionForm" (ngSubmit)="onSubmit()">
<div class="field">
<!--Checkbox for remaining anonymous-->
<label class="checkbox">
<input type="checkbox"
formControlName="anonymous">
Remain anonymous
</label>
</div>
<div class="field" [hidden]="anonymous.value">
<!--Checkbox for remaining anonymous-->
<input class="input"
type="text"
placeholder="Name"
formControlName="name"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="name.invalid && !name.pristine">Name should be 50 characters or less.</div>
</div>
<div class="field">
<!--Checkbox for being contacted-->
<label class="checkbox">
<input type="checkbox" formControlName="contact">
Contact me
</label>
</div>
<div class="field" [hidden]="!contact.value">
<!--Field for email-->
<input class="input"
type="text"
placeholder="Email"
formControlName="email"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="email.invalid && !email.pristine">
<p *ngIf="email.errors.email">Valid email is required.</p>
</div>
</div>
<div class="field">
<!--Field for content warnings-->
<input class="input"
type="text"
placeholder="Content warnings"
formControlName="name"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="contentWarning.invalid && !contentWarning.pristine">Content warnings are required.</div>
</div>
<div class="field">
<!--Field for title-->
<input class="input"
type="text"
placeholder="Title"
formControlName="title"
[ngClass]="{'form-submitted': formSubmitted}">
<div class="help-is-danger" *ngIf="title.invalid && !title.pristine">Title is required.</div>
</div>
<div class="field">
<!--Text area for message-->
<textarea class="textarea"
type="text"
placeholder="Your Submission"
formControlName="message">
</textarea>
<div class="help-is-danger" *ngIf="message.invalid && !message.pristine">
<p *ngIf="message.errors.minlength">Message is required and should be at least 10 characters.</p>
</div>
</div>
<div class="field">
<!--Checkbox for agree-->
<label class="checkbox">
<input type="checkbox"
formControlName="agree">
Agree
</label>
<div class="help-is-danger" *ngIf="submissionForm.hasError('required', 'agree') && !agree.pristine">Must check 'Agree' in order to submit.</div>
</div>
<!--Submit Button-->
<button type="submit" class="button is-danger is-outlined" [disabled]="!agree.valid && !submissionForm.valid">Submit</button>
</form>
</div>
</div>
</section>
I am currently using Angular 9 and the Bulma framework.
Upvotes: 3
Views: 19651
Reputation: 9377
Because you have fields that must follow some rules only when they are visible, and their visibility state depends on the value of other controls, you must write some custom validators. You have two ways of doing that: applying the custom validators directly to the controls, or applying them to the form. Both of them are kind of verbose and I'll put here the one I think is easier to understand, which is applying to the form.
It's important to notice that I'm just answering your requirement for validators. It doesn't mean this is the best or easiest way to achieve the UX you want.
createForm(): void {
this.submissionForm = this.fb.group(
{
...
name: [''], // <= no validators here
...
email: [''], // <= no validators here
...
},
// apply two custom validators to the form
{ validators: [validateName, validateEmail] }
);
}
function validateEmail(formGroup: FormGroup): ValidationErrors {
const anonymousCtrl = formGroup && formGroup.get('contact');
if (!anonymousCtrl || !anonymousCtrl.value) {return null;}
const control = formGroup.get('email');
const regex = /^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/;
const emailMalformed = !regex.test(control.value);
const emailRequired = !control.value;
let error = {};
if (emailMalformed) error = { ...error, emailMalformed: true };
if (emailRequired) error = { ...error, emailRequired: true };
return Object.keys(error).length ? error : null;
}
function validateName(formGroup: FormGroup): ValidationErrors {
const anonymousCtrl = formGroup && formGroup.get('anonymous');
if (!anonymousCtrl || anonymousCtrl.value) {return null;}
const control = formGroup.get('name');
const nameMaxLength = control.value && control.value.length > 250;
const nameRequired = !control.value;
let error = {};
if (nameMaxLength) error = { ...error, nameMaxLength: true };
if (nameRequired) error = { ...error, nameRequired: true };
return Object.keys(error).length ? error : null;
}
<input formControlName="name">
<div class="help-is-danger"
*ngIf="nameInvalid && !name.pristine">
Name should be 50 characters or less.
</div>
...
<input formControlName="email">
<div class="help-is-danger"
*ngIf="emailInvalid && !email.pristine">
A valid email is required.
</div>
nameInvalid
and emailInvalid
:get nameInvalid() {
return (
this.submissionForm.getError('nameRequired') ||
this.submissionForm.getError('nameMaxLength')
);
}
get emailInvalid() {
return (
this.submissionForm.getError('emailRequired') ||
this.submissionForm.getError('emailMalformed')
);
}
Upvotes: 5
Reputation: 126
even though you hide these field, the form still demand for value to met their validator rules.
to disable validator in name, email field you need to disable the reactive form field
if (this.submissionForm.get('anonymous').value == false) {
this.submissionForm.get('name').enable()
}
else{
this.submissionForm.get('name').disable();
}
it will be better to use this if else in rxjs way
this.submissionForm.get('anonymous').valueChanges.subscribe(
value => {
if (value == false) {
this.submissionForm.get('name').enable()
}
else{
this.submissionForm.get('name').disable();
}
}
)
Upvotes: 3