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