1

hi I am trying to create a set of custom reactive form controls for easy usage in templates. so far I have managed to create one using ControlValueAccessor interface. the editing of form is working fine but I am not able to display error messages with it. can anyone suggest a way to pass and display error message in the custom controls html

here is my code

input-form.component.html

<div style="font-size: 12px">
  <mat-form-field appearance="outline">
    <mat-label>{{ label }}</mat-label>
    <input
      matInput
      [required]="required"
      [readonly]="readonly"
      [formControl]="textInputFormControl"
      (input)="change()"
    />
  </mat-form-field>
</div>

input-form.component.ts

import { Component, Input, OnChanges } from '@angular/core';
import {
  ControlValueAccessor,
  FormControl,
  NG_VALUE_ACCESSOR,
} from '@angular/forms';
import { getErrorMessage } from '../form-fields.helper';

@Component({
  selector: 'x-text-input',
  templateUrl: './text-input.component.html',
  styleUrls: ['./text-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi: true,
      useExisting: TextInputComponent,
    },
  ],
})
export class TextInputComponent implements ControlValueAccessor {
  

  public textInputFormControl = new FormControl('');
  onTouched = () => {};
  onChange: (_: string) => {};

  @Input()
  public label: string;

  @Input()
  public readonly: boolean;


  public change() {
    this.onChange(this.textInputFormControl.value);
    this.onTouched();
  }

  writeValue(value: any): void {
    this.textInputFormControl.setValue(value);
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(disabled: boolean) {
    disabled
      ? this.textInputFormControl.disable({ emitEvent: false })
      : this.textInputFormControl.enable({ emitEvent: false });
  }
}

thanks!

  • When we have a custom form with a mat-input, we need take account that the error "belong" to the "formControl", not to the "mat-input". One aproach is use a [custom error](https://material.angular.io/components/input/overview#changing-when-error-messages-are-shown) matcher like this [SO](https://stackoverflow.com/questions/58459617/component-for-wrap-angular-material-input-does-not-show-error-styles/58472470#58472470) or [this another](https://stackoverflow.com/questions/56887035/custom-controls-with-reactive-forms/56893298#56893298). – Eliseo Jun 10 '22 at 06:50
  • The other option is create a [custom control field](https://material.angular.io/guide/creating-a-custom-form-field-control) (a good link step-to-step) is also [here](https://itnext.io/creating-a-custom-form-field-control-compatible-with-reactive-forms-and-angular-material-cf195905b451) – Eliseo Jun 10 '22 at 06:50
  • @Eliseo that's the same link I provided in the edit section of my answer, which was accepted and then unaccepted by the OP for some reason. – Vasileios Kagklis Jun 10 '22 at 16:03
  • @VasileiosKagklis, Athul ask about a custom form control that use inside a **matInput**. To create a kind of this form controls you need change the error matcher (your idea of use [ngClass] is a brilliant idea, but it's not about a mat-input). The another option is create a custom form control "at marerial style". For this your should extends (futhermore ControlAccesor) from MatFormFieldControl: add a controlType, stateChanged,errorState, placeholder... – Eliseo Jun 13 '22 at 06:16

2 Answers2

0

Your custom form control doesn't have to be reactive forms specific. It can be form-module agnostic. Also, if you need validations, you will need to implement the Validator interface.

Here is a StackBlitz demo that demonstrates how to do this. See the required-field component.

You can also check this article that I wrote, which explains extensively how to create a custom form control with validations and all.

EDIT:

To answer your question in the comment, there is this guide in Material's official documentation. Frankly speaking, I've never implemented a custom control with Material, but I don't see why you wouldn't be able to inject NgControl and omit the formControl, like in the StackBlitz demo.

Vasileios Kagklis
  • 784
  • 1
  • 4
  • 14
0

Just discovered that In Angular 14 you can do:

  public ngControl: NgControl = Object.assign(inject(NgControl), {
    valueAccessor: this,
  });

on the class implementing ControlValueAccessor, so you can remove providers array, constructor and even extend from this class. And if you forget to apply formControl on such component, it will throw relevant errors, that runtime cannot read CVA methods (because no control to set as accessor).

Maxime Lyakhov
  • 140
  • 1
  • 11