I'm trying to make a wrapper component for a color picker using ngx-color-picker
and Angular Materials FormField
. I configured it as required
but it's outline doesn't turn red when invalid like the other form fields... How can I achieve this behavior on my custom form field?
color-picker.component.ts
import { Component, Input, forwardRef } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
// inspiration:
// https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html
export const requiredValidator = (c: FormControl): ValidationErrors | null => {
return
c.value === '' ||
c.value === null ||
c.value === undefined ||
c.value.length === 0
? {
required: {
valid: false,
},
}
: null
}
@Component({
selector: 'nda-color-picker',
templateUrl: './color-picker.component.html',
styleUrls: ['./color-picker.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ColorPickerComponent),
multi: true,
},
],
})
export class ColorPickerComponent implements ControlValueAccessor, Validator {
@Input() label: string
_color: string
get color() {
return this._color
}
set color(value: string) {
this._color = value
this.propagateChange(this._color)
}
constructor() {}
writeValue(value: any) {
if (value !== undefined) {
this.color = value
}
}
propagateChange = (_: any) => {}
registerOnChange(fn) {
this.propagateChange = fn
}
registerOnTouched() {}
validate(control: AbstractControl): ValidationErrors | null {
return requiredValidator(control as FormControl)
}
}
color-picker.component.html
<mat-form-field appearance="outline" [floatLabel]="'always'">
<mat-label>{{ label }}</mat-label>
<input
matTooltip="{{ color }}"
cpOKButton="true"
cpCancelButton="true"
[required]="required"
matInput
[ngStyle]="{ color: color, background: color }"
[colorPicker]="color"
(colorPickerChange)="color = $event"
/>
</mat-form-field>
form-component.html
<form
[formGroup]="transportForm"
autocomplete="off"
(ngSubmit)="handleSubmit()"
>A
<div mat-dialog-content>
<div fxLayout="row wrap" fxLayoutGap="1rem">
<mat-form-field appearance="outline">
<mat-label>Icon</mat-label>
<input
matInput
placeholder="Icon"
formControlName="icon"
required
/>
</mat-form-field>
<nda-color-picker
label="Badge Color Code"
formControlName="badgeColorCode"
required
></nda-color-picker>
<nda-color-picker
label="Text Color Code"
formControlName="textColorCode"
required
></nda-color-picker>
<mat-form-field appearance="outline">
<mat-label>Badge Type</mat-label>
<input
matInput
placeholder="Badge Type"
formControlName="badgeType"
/>
</mat-form-field>
</div>
</div>
<!-- dialog actions -->
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="space-between center">
<div>
<button
type="button"
*ngIf="data && data.code"
color="warn"
mat-flat-button
(click)="handleDelete()"
>
DELETE
</button>
</div>
<div class="btn-container">
<button type="reset" mat-stroked-button (click)="handleCancel()">
CANCEL
</button>
<button
mat-flat-button
color="accent"
[disabled]="transportForm.pristine"
>
SAVE
</button>
</div>
<div
*ngIf="!transportForm.valid"
class="mat-small dialog-error-message"
>
* Fill out all required fields
</div>
</div>
</form>
------ UPDATE ------
I updated the color-picker.component
with a validator function and class implementing Validator interface. The validator function is actually called and works as expected. But the form field isn't marked as invalid as reactive form fields normally are (css class .ng-invalid
).