Setting a Validator for a custom form control in Angular: A step-by-step guide

I need to apply validators to a specific control in formGroup from outside of a custom control component:

<form [formGroup]="fg">
    <custom-control formControlName="custom">
    </custom-control>
</form>
this.fg = this.fb.group({
      custom: [null,Validator.required]
});

This is the custom control component:

@Component({
  selector: 'custom-control',
  template: `
  <mat-form-field>
        <input [formControl]="inputFormControl" />
        <mat-error 
           *ngIf="inputFormControl.hasError('required')">Required</mat-error>
  </mat-form-field>
  `,
  styleUrls: ["custom-control.component.scss"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi:true,
      useExisting: CustomControlComponent
    }
  ]
})
export class CustomControlComponent implements ControlValueAccessor {

  inputFormControl: new FormControl();
  onChange = (quantity) => {};

  onTouched = () => {};

  writeValue(quantity: number) {
    this.quantity = quantity;
  }

  registerOnChange(onChange: any) {
    this.onChange = onChange;
  }

  registerOnTouched(onTouched: any) {}
}

How can I display the required error? and What if I want to set another validator using fg.get('custom').setValidator(XXX)? Thanks!

Answer №1

To utilize the mat-error feature:

  • The mat-error element should be within a mat-form-field that contains the input triggering the error
  • The FormControl inside the mat-form-field must correspond exactly to the validated FormControl

In your current setup, you will need to enclose your custom-control in a mat-form-field and place the mat-error within it instead of having the mat-error nested inside the custom-component.

Answer №2

create a CustomControlComponent that implements the Validator interface and provides NG_VALIDATORS

 @Component({
  selector: 'custom-control',
  template: `
        <input [formControl]="inputFormControl" />
        <mat-error *ngIf="inputFormControl.hasError('required')">Required</mat-error>
  `,
  styleUrls: ["custom-control.component.scss"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi:true,
      useExisting: CustomControlComponent
    },
    {
        provide: NG_VALIDATORS,
        useExisting: forwardRef(() => CustomControlComponent),
        multi: true,
    },
  ]
})
export class CustomControlComponent implements ControlValueAccessor, Validator {

  inputFormControl = new FormControl();
  onChange = (quantity) => {};

  onTouched = () => {};

  writeValue(quantity: number) {
    this.quantity = quantity;
  }

  registerOnChange(onChange: any) {
    this.onChange = onChange;
  }

  registerOnTouched(onTouched: any) {}

  onValidatorChange = () => {
  };

  registerOnValidatorChange(fn: () => void): void {
    this.onValidatorChange = fn;
   }

  validate(c: AbstractControl): ValidationErrors | null {
    return this.inputFormControl.invalid ? { internal: true } : null;
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

After deploying the application, the 'Access-Control-Allow-Origin' setting was not found

After deploying the same application to 2 servers (one for testing and one for production) on IIS, I encountered an issue. The application works perfectly on the first server, but not on the second one. While I am able to connect, retrieve data, and add ...

Passing data to a child component using Context in React is not working

I have a parent component where I am storing data in an array of objects and then passing it to another component through Context. Here is how my parent component looks: export type HistoryData = { input: string; date: string; ...

"Upon subscribing, the object fails to appear on the screen

Why is the subscription object not displaying? Did I make a mistake? this.service.submitGbtForm(formValue) .subscribe((status) => { let a = status; // a = {submitGbtFrom: 'success'} console.log(a, 'SINGLE ...

What is the reason behind Angular not allowing users to define @Output events that begin with 'on'?

While developing a component, I defined an output EventEmitter named onUploaded. However, Angular flagged an error instructing me to use (uploaded) instead. This restriction is due to security concerns, as bindings starting with 'ono' pose risks. ...

Uh oh, it looks like there's an issue! The function getCoordinates is not recognized for this.locations

Whenever I attempt to execute the method in my class (getCoordinates()), an Error is thrown: ERROR TypeError: this.locations[0].getCoordinates is not a function What am I doing wrong? The service method I'm using: getLocations(){ return this ...

Unable to configure unit tests for Vue project using Typescript due to TypeError: Unable to destructure property `polyfills` of 'undefined' or 'null'

I've been working on adding unit tests for an existing Vue project that uses Typescript. I followed the guidelines provided by vue-test-utils for using Typescript, but when I ran the test, I encountered an error message stating: TypeError: Cannot d ...

The @angular/fire library encountered a FirebaseError stating that the first parameter in the collection() function should be either a CollectionReference, a DocumentReference, or a

I am encountering an error while attempting to access a firestore collection. Despite exploring various solutions on stackoverflow, I have not been able to resolve this issue. The code has been implemented with AngularFire Cloud Firestore document. For mo ...

Error encountered in Angular 7.2.0: Attempting to assign a value of type 'string' to a variable of type 'RunGuardsAndResolvers' is not allowed

Encountering an issue with Angular compiler-cli v.7.2.0: Error message: Types of property 'runGuardsAndResolvers' are incompatible. Type 'string' is not assignable to type 'RunGuardsAndResolvers' This error occurs when try ...

Angular patch value not functioning properly after initial use

Whenever I click on the edit icon, I want the form field to populate. It works correctly the first time, but subsequent clicks on different icons do not update it. However, if I hit the cancel button and then click on any edit button again, it works fine. ...

What is the process for accessing NGXS selectors during unit testing?

When subscribing to the selector teamMemberService$, we expect a list to be returned, followed by some checks. I need to write tests for this code snippet, but I am unsure how to approach testing this type of subscription. if (this.doctor != undefined) ...

Unable to generate Angular project using the command "ng new app_name" due to error code -4058

Whenever I try to run the command ng new app-name, I encounter error -4058. If I execute the same command while opening cmd as an administrator in the directory C:/Windows/system32, the project creation process goes smoothly. However, if I change the dire ...

Tips for utilizing ngIf based on the value of a variable

Here is the code from my file.html: <button ion-button item-right> <ion-icon name="md-add-circle" (click)="save();"></ion-icon> </button> The content of file.ts is: editmode = false; I am trying to achieve the foll ...

The Angular Date Pipe is displaying an incorrect date after processing the initial date value

Utilizing Angular's date pipe within my Angular 2 application has been beneficial for formatting dates in a user-friendly manner. I have successfully integrated API dates, edited them, and saved the changes back to the API with ease. However, an issue ...

Comparison between Destructuring in TypeScript and JavaScript

Starting my journey with TypeScript after a background in JavaScript. In the realm of modern JavaScript, one can easily destructure objects like this: let {n,s} = {n:42, s:'test'}; console.log(n, s); // 42 test Assuming TypeScript follows su ...

Managing errors when dealing with Observables that are being replayed and have a long lifespan

StackBlitz: https://stackblitz.com/edit/angular-ivy-s8mzka I've developed an Angular template that utilizes the `async` pipe to subscribe to an Observable. This Observable is generated by: Subscription to an NgRx Store Selector (which provides sele ...

Leveraging the Map function with Arrays in TypeScript

Is there a way to dynamically render JSON data into a component using array.map in Typescript? I am running into an error with the code snippet below. const PricingSection: FC<IProps> = ({ icon, title, price, user, observations, projects, intervie ...

Is there a method to enhance the efficiency of generating the code coverage report in VSTS?

What are the possible reasons for the extended duration (>1 min) required to generate the code coverage report when running the associated command in VSTS? Are there any strategies that can be implemented to streamline this process? ...

Converting Javascript tools into Typescript

I'm currently in the process of migrating my Ionic1 project to Ionic2, and it's been quite an interesting journey so far! One challenge that I'm facing is how to transfer a lengthy list of utility functions written in JavaScript, like CmToFe ...

"Fixing the cubic-bezier for the exiting animation ends up causing issues with the entering

Trying to implement a collapsing list animation using React/Joy-UI. Below is the Transition element code snippet: <Transition nodeRef={nodeRef} in={browseOpen} timeout={1000}> {(state: string) => (<List aria-labelledby="nav-list-bro ...

Exploring the process of importing and exporting modules in TypeScript with the help of systemjs

Is there a way to export a module using systemjs in TypeScript? I encountered the error: TS1148 cannot compile modules unless the '--module' flag is provided. Here's my code; animal.ts export class Animal { color: string; age: numb ...