I'm using angular 4 forms and I have some fields. and first_name, last_name and company are really important for me. I want to force the user to fill one of these 3 fields. how can I do it?
here are .ts codes:
this.contactForm = this.fb.group({
first_name: [null, Validators.compose([Validators.required])],
last_name: [null, Validators.compose([Validators.required])],
email: [null, Validators.compose([this.validateEmail])],
company: [null],
position: [null],
});
an html:
<form [formGroup]="contactForm" fxLayout="column">
<md-input-container class="data_light">
<input class="data_font capital" mdInput placeholder="{{'Contacts.FirstName' | translate}}" [formControl]="contactForm.controls['first_name']">
</md-input-container>
<small *ngIf="contactForm.controls['first_name'].hasError('required') && contactForm.controls['first_name'].touched" class="mat-text-warn data_light">{{'Contacts.firstNameReq' | translate}}
</small>
<md-input-container class="data_light">
<input class="data_font capital" mdInput placeholder="{{'Contacts.lastName' | translate}}" [formControl]="contactForm.controls['last_name']">
</md-input-container>
<small *ngIf="contactForm.controls['last_name'].hasError('required') && contactForm.controls['last_name'].touched" class="mat-text-warn data_light">{{'Contacts.lastNameReq' | translate}}
</small>
<md-input-container class="data_light">
<input class="data_font" mdInput placeholder="{{'Contacts.email' | translate}}" [formControl]="contactForm.controls['email']"
(blur)="checkContactEmail()">
</md-input-container>
<small *ngIf="contactForm.controls['email'].hasError('validateEmail') && contactForm.controls['email'].dirty" class="mat-text-warn data_light">{{'Contacts.emailValid' | translate}}
</small>
<small *ngIf="!emailValid" class="mat-text-warn data_light">{{emailMessage}}
</small>
<md-input-container class="data_light">
<input class="data_font capital" mdInput placeholder="{{'Contacts.Company' | translate}}" [formControl]="contactForm.controls['company']">
</md-input-container>
<md-input-container class="data_light">
<input class="data_font capital" mdInput placeholder="{{'Contacts.Position' | translate}}" [formControl]="contactForm.controls['position']">
</md-input-container>
</form>
if one field in the form is filled,all other fields must be filled or if none of the fields are filled then no need of checking validations. Iam using a method focus to add validations. once selecting a field,all fields are required validation executing.
Angular provides two different approaches to handling user input through forms: reactive and template-driven.
Overview for form-field. <mat-form-field> is a component used to wrap several Angular Material components and apply common Text field styles such as the underline, floating label, and hint messages.
private atLeastOneValidator = () => {
return (controlGroup) => {
let controls = controlGroup.controls;
if ( controls ) {
let theOne = Object.keys(controls).find(key=> controls[key].value!=='');
if ( !theOne ) {
return {
atLeastOneRequired : {
text : 'At least one should be selected'
}
}
}
}
return null;
};
};
Above is a reusable validator and you can use it like this :
this.contactForm.setValidators(this.atLeastOneValidator())
This makes the whole contactForm invalid if none of the fields have a value.
Note that the contactForm's default validation will still work nicely and you don't have to care about the number of fields in your form and everything is dynamically handled
EDIT :
Note that the atLeastOneValidator is checking for the values to not to be empty , but if you wanted to say :
At least one of them fields must be valid, then you can simply adjust the conditions to below
let theOne = Object.keys(controls).find(key=> controls[key].valid );
You can then use the error in your template like this :
<small *ngIf="contactForm.hasError('atLeastOneRequired')" class="mat-text-warn data_light">{{contactForm.getError('atLeastOneRequired')}}
</small>
Disable your button until required fields are not fileld by user
<button type='submit' [disabled]='!contactForm.valid'> Submit</button>
Then call function to check disable like this
<button type='submit' [disabled]='checkValid()'> Submit</button>
checkValid() {
if(this.contactForm.get('first_name').valid || this.contactForm.get('last_name').valid || this.contactForm.get('email').valid) {
return false;
} else {
return true;
}
}
I provide a better customized way for some fields only, and not for all fields of the form. Also,
Not like this <button [disabled]="checkValid()">,
But to use directly <button [disabled]="form.invalid || form.pristine || loading">
Fields can be provided via Array param as following:
export const atLeastOneHasValue = (fields: Array<string>) => {
return (group: FormGroup) => {
for (const fieldName of fields) {
if (group.get(fieldName).value) {
return null;
}
}
return { paymentMethodRequired: true };
};
};
// Form group
form = this.formBuilder.group({
field1: [],
field2: [],
field3: [],
field4: []
}, {
validators: atLeastOneHasValue(['field2', 'field3'])
});
You then, can check and show error as following in the template, saving button will be disabled thanks to form.invalid boolean:
<mat-error *ngIf="form.hasError('paymentMethodRequired')">
{{"ERROR_PAYMENT_METHOD_REQUIRED" | translate}}
</mat-error>
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