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>
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>