I'm trying to create a contact form. The form looks like this:
<form novalidate [formGroup]="contact" (ngSubmit)="send()">
<p>
<label>Name
<br>
<input type="text" class="input" value="" formControlName="name">
<span class="error">Enter your name</span>
</label>
</p>
<p>
<label>E-mail
<br>
<input type="email" class="input" value="" formControlName="email">
<span class="error">It looks like this email is invalid</span>
</label>
</p>
<p>
<label>Phone
<br>
<input type="text" class="input" value="" formControlName="telefone">
<span class="error">It looks like this phone number is invalid</span>
</label>
</p>
<p>
<label>Message
<br>
<textarea type="text" class="input" value="" formControlName="message"></textarea>
<span class="error">The message can't be empty</span>
</label>
</p>
<p class="submit">
<input type="submit" name="submit" class="bt" value="Send">
</p>
</form>
In this form only the message and the name and email or the phone number fields should be required.
I'm using a formBuilder
class, so here's the TypeScript code:
this.contact = this.formBuilder.group({
name: ['', Validators.required],
email: ['', Validators.compose([/*Use custom validador??*/])],
phone: ['', Validators.compose([/*Use custom validador??*/]],
message: ['', Validators.required]
});
I tried using custom validators as a solution, but I could not figure out a solution. Any suggestions?
I created a custom validator directive:
import {
FormGroup,
ValidationErrors,
ValidatorFn,
Validators,
} from '@angular/forms';
export const atLeastOne = (validator: ValidatorFn, controls:string[] = null) => (
group: FormGroup,
): ValidationErrors | null => {
if(!controls){
controls = Object.keys(group.controls)
}
const hasAtLeastOne = group && group.controls && controls
.some(k => !validator(group.controls[k]));
return hasAtLeastOne ? null : {
atLeastOne: true,
};
};
To use it, you just do this:
this.form = this.formBuilder.group({
name: ['', Validators.required],
email:[''],
telefone:[''],
message:['', Validators.required],
}, { validator: atLeastOne(Validators.required, ['email','telefone']) });
So email
or telefone
would be required here. If you leave it empty then any control with a value is fine and you can use it with any type of validator, not just Validators.required
.
This is reusable in any form.
Yes, a custom validator is the way to go.
Make your form group like this:
this.contact = this.formBuilder.group({
name: ['', Validators.required],
email: ['', Validators.required],
phone: ['', Validators.required],
message: ['', Validators.required]
}, {validator: this.customValidationFunction})
Then have the customValidationFunction
check for validation. Made up validation just for example:
customValidationFunction(formGroup): any {
let nameField = formGroup.controls['name'].value; //access any of your form fields like this
return (nameField.length < 5) ? { nameLengthFive: true } : null;
}
Change each input like this (changing your p tags to divs. Substitute the control name for each and change syntax for the hidden span tag validation where appropriate):
<div [ngClass]="{'has-error':!contact.controls['name'].valid && contact.controls['name'].touched}">
<label>Name</label>
<input class="input" type="text" [formControl]="contact.controls['name']">
<span [hidden]="!contact.hasError('nameLengthFive')" class="error">Enter your name</span>
</div>
I have updated my validator snippet to support string and number types
I was inspired by Todd Skelton. This is a very simple Validator that does just what you ask for and nothing else:
/**
* Validates if at least one of the provided fields has a value.
* Fields can only be of type number or string.
* @param fields name of the form fields that should be checked
*/
export function atLeastOne(...fields: string[]) {
return (fg: FormGroup): ValidationErrors | null => {
return fields.some(fieldName => {
const field = fg.get(fieldName).value;
if (typeof field === 'number') return field && field >= 0 ? true : false;
if (typeof field === 'string') return field && field.length > 0 ? true : false;
})
? null
: ({ atLeastOne: 'At least one field has to be provided.' } as ValidationErrors);
};
}
And here is how I use it:
ngOnInit(): void {
this.form = this.formBuilder.group(
{
field: [this.field],
anotherField: [this.anotherField],
},
{ validator: atLeastOne('field','anotherField') },
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With