I've created a custom mat-checkbox component and implemented the ControlValueAccessor interface in order to communicate with the passed formControl or ngModel.
The checkbox updates the assigned formControl on change, however my problem is that it doesn't seem to register validator functions either template-driven or the reactive way.
This is my component:
import {Component, Input, OnInit, Self, ViewEncapsulation} from '@angular/core';
import {ControlValueAccessor, NgControl} from '@angular/forms';
@Component({
selector: 'app-checkbox',
templateUrl: './checkbox.component.html',
styleUrls: ['./checkbox.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class CheckboxComponent implements OnInit, ControlValueAccessor {
@Input() id: string;
@Input('aria-label') ariaLabel: string;
@Input() label: string;
private isChecked = false;
private isDisabled = false;
onChange = (isChecked: boolean) => {};
onTouched = () => {};
constructor(@Self() public controlDir: NgControl) {
// providing component as ControlValueAccessor
controlDir.valueAccessor = this;
}
ngOnInit() {
// Initializing assigned FormControl and providing it's Validator
const control = this.controlDir.control;
control.setValidators(control.validator);
control.updateValueAndValidity();
}
/**
* ControlValueAccessor interface (https://angular.io/api/forms/ControlValueAccessor)
* Registers a callback function that is called when the control's value changes in the UI.
* @param fn callback function
*/
registerOnChange(fn: (value: boolean) => void): void {
this.onChange = fn;
}
/**
* Input change callback that passes the changed string to this.onChange method
*/
valueChanged(value: boolean) {
this.onChange(value);
}
/**
* ControlValueAccessor interface
* Registers a callback function is called by the forms API on initialization to update the form model on blur.
* @param fn callback function
*/
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
/**
* (optional) - ControlValueAccessor interface
* Function that is called by the forms API when the control status changes to or from 'DISABLED'. Depending on the status, it enables or disables the appropriate DOM element.
* @param isDisabled control state
*/
setDisabledState(isDisabled: boolean): void {
this.isDisabled = isDisabled;
}
/**
* ControlValueAccessor interface
* Writes a new value to the element.
* @param value new control value
*/
writeValue(value: boolean): void {
if (value) {
this.isChecked = value;
}
}
}
And this my template:
<mat-checkbox color="primary" [checked]="isChecked" [disabled]="isDisabled" [id]="id" [attr.aria-label]="ariaLabel" (change)="valueChanged($event.checked)" (blur)="onTouched()">
{{label}}
</mat-checkbox>
This is how I call my component the template-driven way:
<form class="row border-bottom" #checkboxForm2="ngForm">
<div class="col-12 col-md-4 my-1">
checkboxForm2.value: <br>
{{checkboxForm2.value | json}} <br>
checkboxForm2.valid: {{checkboxForm2.valid}}
</div>
<div class="col-12 col-md-8 my-3">
<app-checkbox [ngModel]="false" name="checkbox3" label="REQUIRED" required></app-checkbox>
</div>
</form>
checkboxForm2.valid
always stays true. How can I make the required validator work?
console.log(control.validator)
and make sure required is there? – sabithpockeronChange()
doing nothing in your custom control? – sabithpockerrequiredTrue
validator, not sure ifrequired
works when you have the value asfalse
– sabithpockeronChange()
was originally set on the change event of my checkbox. I just played around with the valueChanged function. – Boldizsar