I have some problem with custom validation in Angular 4 using FormGroup
and FormControl
.
I have page with multiple checkboxes, in this example 6 of them. Three of them are required all time, and another three os required only if one or two of them was selected. If none of them is selected you can pass validation.
I am initializing my FormGroup in ngOnInit method and it looks like that
ngOnInit() {
let statements = new FormGroup({});
for (let i = 0; i < this.characters.length; i++) {
const element = this.characters[i];
statements.addControl(element.surname, new FormControl(false, CustomValidator.required));
}
statements.addControl(this.firstPerson.surname, new FormControl(false, CustomValidator.requiredPredicate(
//here I have problem to write predicate which check if thirdPerson or secondPerson checbox was selected
));
statements.addControl(this.secondPerson.surname, new FormControl(false, CustomValidator.requiredPredicate(
//here I have problem to write predicate which check if firstPerson or third checbox was selected
));
statements.addControl(this.thirdPerson.surname, new FormControl(false, CustomValidator.requiredPredicate(
//here I have problem to write predicate which check if firstPerson or secondPerson checbox was selected
));
this.fullData = new FormGroup({
//here I have more than statemets, thats why I have fullData
statements: statements
});
}
Here for loop is used to populate all of required checbkox. But I thought I cannot do it for thos last three, where I need to check if other two were selected. Inside of predicate I have no idea, how could I check if other two FormControls(checkboxes) was selected.
View for this components looks like that
<form [formGroup]="fullData">
<div formGroupName="statements">
<p>All of these 3 are required</p>
<div *ngFor="let character of characters">
<custom-checkbox
[controlName]="character.surname"
[formGroup]="fullData.get('statements')"
[id]="character.name"
[label]="character.name + ' ' + character.surname">
</custom-checkbox>
</div>
<br />
<br />
<p>Only required when one of them is clicked</p>
<custom-checkbox
[controlName]="firstPerson.surname"
[formGroup]="fullData.get('statements')"
[id]="firstPerson.name"
[label]="firstPerson.name + ' ' + firstPerson.surname">
</custom-checkbox>
<custom-checkbox
[controlName]="secondPerson.surname"
[formGroup]="fullData.get('statements')"
[id]="secondPerson.name"
[label]="secondPerson.name + ' ' + secondPerson.surname">
</custom-checkbox>
<custom-checkbox
[controlName]="thirdPerson.surname"
[formGroup]="fullData.get('statements')"
[id]="thirdPerson.name"
[label]="thirdPerson.name + ' ' + thirdPerson.surname">
</custom-checkbox>
<button type="button" (click)="onSubmit(fullData)"> Submit </button>
</div>
</form>
I am using here custom checbkox, because I want use it in more places and extend it. CustomValidation class is something like this.
export interface ValidationError {
[msg:string] : boolean;
}
export class CustomValidator {
static required(control: any): ValidationError {
if(control.value && (control.value.length === undefined || control.value.length >0)) {
return null;
}
return {"VALIDATION.REQUIRED": false};
}
static requiredPredicate(predicate: any): ValidatorFn {
return function(control: any){
const value = predicate();
if(value){
return CustomValidator.required(control);
}
return null;
}
}
}
Any idea how can I do it?
CustomChecbkox code:
export class CustomCheckboxComponent implements OnInit, OnDestroy {
@Input('label') label: string;óó
@Input('controlName') controlName: string;
@Input('formGroup') formGroup: FormGroup;
@Input('id') id: string;
@ViewChild('inputField') inputField: any;
@ViewChild('surroundingDiv') surroundingDiv: any;
isValid: boolean = true;
fieldUpdated: boolean = false;
shouldDisplayError: boolean = false;
displayError: boolean = false;
constructor(public cd: ChangeDetectorRef) {
}
ngOnDestroy(): void {
}
fieldChanged(value) {
if (!this.fieldUpdated) {
this.fieldUpdated = true;
this.validateField(value);
}
}
validateField(value): boolean {
if (!this.fieldUpdated) {
return true;
}
this.isValid = this.formGroup.get(this.controlName).errors ? false : true;
if ((this.fieldUpdated) && !this.shouldDisplayError) {
this.shouldDisplayError = true;
}
this.displayError = !this.isValid && this.shouldDisplayError;
return !this.displayError;
}
ngOnInit() {
}
}
and html
<div class="custom-checkbox"
[formGroup]="formGroup"
#surroundingDiv
[ngClass]="{'custom_validation' : displayError}">
<label class="mdl-checkbox mdl-checkbox--pink mdl-js-checkbox mdl-js-ripple-effect"
for="{{id}}">
<input class="mdl-checkbox__input"
type="checkbox"
id="{{id}}"
formControlName="{{controlName}}"
(change)="fieldChanged($event.target.value)"
#inputField
>{{ label }}
</label>
<div class="custom-validation__error" *ngIf="displayError" style="padding-left: 24px;">
<span *ngFor="let error of formGroup.get(controlName).errors "
>{{error}}</span>
</div>
</div>