Fetching FormControl from Directive in Angular

Is there a way to dynamically add validators to a FormControl through a custom Directive?

@Directive({
    selector: "[idNumber]",
})
export class IdNumberDirective implements OnInit {

    constructor(private formControl: FormControl) { }

    ngOnInit() {
        this.addValidators(this.formControl);
    }

    addValidators(formControl: FormControl) {
        formControl.setValidators(Validators.compose(
            [Validators.required,
            Validators.minLength(3),
            Validators.maxLength(8)
            ]
        ));
    }



<mat-form-field>
    <mat-label>{{label}}</mat-label>
    <input matInput
        [formControl]="idNumberFormControl"
        [placeholder]="placeholder"
</mat-form-field>


I want to avoid using ElementRef to reference the native element.
Instead, I prefer to work with the formControl directly...
...The desired usage would be as follows:

// Using my 'idNumber' directive in HTML ////////
<custom-input-string
    idNumber 
    [name]="'idNumber'"
    [label]="Id Number"
    [placeholder]="">
</custom-input-string>

// In TypeScript file ////////
@ViewChild(CustomInputStringComponent) child: CustomInputStringComponent;

ngAfterViewInit() {
    setTimeout(() => {
        this.child.insertIntoForm(this.signupForm);
    }, 0);
}


Any thoughts or suggestions?
Many thanks.

Answer №1

Learn how to enhance your form control with validators using a directive.

Check out the code on Stackblitz

Keep in mind that implementing this may overwrite any existing validators you have set up.

constructor(
  // Access the control directive
  private control: NgControl
) { }
ngOnInit() {
  const abstractControl = this.control.control;
  abstractControl && abstractControl.setValidators([Validators.required]);
}

Answer №2

By utilizing NgControl and constructor DI injection, we have the ability to create a directive that can be applied to form controls in both reactive forms using formControlName or template-driven forms:

Custom Directive Example:

import { Directive } from "@angular/core";
import { NgControl } from "@angular/forms";

@Directive({
  selector: '[my-custom-directive]'
})
export class MyCustomDirective {
  constructor(private elementRef: ElementRef, private control : NgControl) { }

}

Answer №3

The most efficient and simplistic method involves utilizing the NG_VALIDATORS provider:

import { Directive } from '@angular/core'
import { NG_VALIDATORS ValidationErrors, Validator } from '@angular/forms'

@Directive({
  selector: '[myIdValidator]',
  providers: [
    { provide: NG_VALIDATORS, useExisting: IdValidatorDirective, multi: true }
  ],
})
export class IdValidatorDirective implements Validator {
  validate(control: AbstractControl): ValidationErrors | null {
    // custom validation logic goes here
    return null
  }
}

Answer №4

If you want to utilize both FormGroup and FormControl within your directive, you can easily achieve this by using FormGroupDirective:

Note that in the following example, I am working on implementing a country selection feature.

import { FormGroupDirective } from "@angular/forms";

Subsequently:

{
constructor(private fg: FormGroupDirective) { }


// Retrieve the FormGroup
console.log('Values of My FormGroup: ', this.fg.value);

// Obtain the FormControl
console.log('The selectedCountryCtrl: ', this.fg.control.controls.selectedCountryCtrl);
console.log('Value of the selectedCountryCtrl: ', this.fg.control.controls.selectedCountryCtrl.value);

// Directly access the variable/object
console.log('Value of my selectedCountry FormControl: ', this.fg.value.selectedCountry);
}

Answer №5

//CustomComponent.ts

import { Component, OnInit } from '@angular/core';
import { FormControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { IdNumberValidator } from '../validators/IdNumberValidator';

@Component({
  selector: 'app-custom',
  templateUrl: './custom.component.html',
  styleUrls: ['./custom.component.css'],
  providers:[IdNumberValidator]
})
export class CustomComponent implements OnInit {
  customForm: FormGroup;

  constructor(fb: FormBuilder, idNumberValidator : IdNumberValidator) { 
    this.customForm = fb.group({
      idFormControl: new FormControl(null,
          Validators.compose([
            Validators.required,
            Validators.minLength(3),
            Validators.maxLength(8),
          idNumberValidator.validate()
          ])
        ),
    })
  }
}

//IdNumberValidator.ts

import { Directive, OnInit } from '@angular/core';
import { Validators, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';

@Directive({
  selector: '[idValidator]'
})
export class IdNumberValidator implements OnInit {

  constructor() {

  }

  ngOnInit() {

  }

  validate(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      if (control.value !== "Sachin") {
        return { "customError": { inValid: true, errMsg: 'Invalid Value' } };
      }
      return null;
    }
  }
}


//custom.component.html

<div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-html lang-html prettyprint-override"><code>    <form [formGroup]="customForm">
      <input idValidator formControlName="idFormControl" />
      <div *ngIf="customForm.get('idFormControl').invalid && customForm.get('idFormControl').errors.customError.inValid"
        style="color:red">
        {{customForm.get('idFormControl').errors.customError.errMsg}}
      </div>
    
      <button type="submit">Submit</button>
    </form>

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

Issue with identifying the standard property `form.invalid` in a template-based form during testing

In my pursuit to test a template driven form, I aim to ensure that the user enters a name by initially disabling the form's button. This is achieved through the use of the 'required' property on the input field for the name. To implement th ...

The initial execution of the "ionViewDidEnter" method in Ionic 2 can be quite sluggish

Could you explain to me why there is a significant delay in loading the below functionality for the first time, but it loads normally on the second attempt? The same delay occurs on the actual device as well. Please refer to the attached video. Note : The ...

Tips for personalizing Bootstrap 4 with angular-cli

I am working on an Angular 4 application using angular-cli 1.0 and Bootstrap 4-alpha6, where I aim to customize the appearance of Bootstrap 4 elements, such as changing the color of primary buttons. My goal is to implement a solution that will remain effe ...

Interpolating strings in a graphQL query

Exploring the world of Gatsby and its graphQL query system for asset retrieval is a fascinating journey. I have successfully implemented a component called Image that fetches and displays images. However, I am facing a challenge in customizing the name of ...

Executing HTTP requests within a loop using AngularJS 2

I have been exploring the functionality of the YouTube API, and I am facing an issue with getting the icons for the first 'n' videos. For this, I need to make a request for each video. My initial thought was to use a for loop to handle these requ ...

Using Typescript: ForOf Iteration with Unknown Value Types

My journey began with a quick peek at this particular inquiry. However, the approach discussed there utilized custom typing. I am currently iterating over object entries using a for-of loop. Here's a snippet of the values I'm dealing with below. ...

Transferring data from a child component to a parent component in Angular using @ViewChild requires providing 2 arguments

Currently, I am attempting to transmit data using @Output & EventEmitter and @ViewChild & AfterViewInit from a child component to a parent component. Below is the code from my parent component .html file: <app-child (filterEvent)=" getValu ...

Guide on displaying information on a pie chart in Angular 2 using ng2-charts

How can I display data on a pie chart like this? https://i.sstatic.net/WX9ptm.png Like shown in the image below: https://i.sstatic.net/sqlv2m.png <canvas baseChart class="pie" [data]="Data" [labels]="Labels" [colors]="Colors" [chartType]="p ...

Is there a way for me to store the current router in a state for later use

I am currently working on implementing conditional styling with 2 different headers. My goal is to save the current router page into a state. Here's my code snippet: const [page, setPage] = useState("black"); const data = { page, setPage, ...

What steps do I need to follow to write this function in TypeScript?

I am encountering a problem when building the project where it shows errors in 2 functions. Can someone please assist me? The first error message is as follows: Argument of type 'IFilmCard[] | undefined' is not assignable to parameter of type &a ...

Unable to utilize external JavaScript files in Angular 8

I've been working on integrating an HTML template into my Angular project and for the most part, everything is going smoothly. However, I've encountered an issue where my JS plugins are not loading properly. I have double-checked the file paths i ...

Maximize the performance of displaying images

At the moment, I have a set of 6 graphics (0,1,2,3,4,5)... The arrangement of these graphics looks fantastic! However, I am facing an issue when a user only has 3 graphics, for example 0, 2, and 5. In this scenario, my graphics do not line up correctly. D ...

Troubleshooting problem with sorting in Angular 4 material header

Using Angular 4 material for a table has presented me with two issues: 1. When sorting a table, it displays the description of the sorting order in the header. I would like to remove this. https://i.sstatic.net/5bHFO.png It displays "Sorted by ascending o ...

Diverse Templates within a Single Angular Component

Is there a way to reuse a single component with varying content? I have a component called quick-links that extends the dashboard-panel component and includes a title and content. I am looking to reuse the quick-links component with different titles and co ...

Issues with Webpack and TypeScript CommonsChunkPlugin functionality not functioning as expected

Despite following various tutorials on setting up CommonsChunkPlugin, I am unable to get it to work. I have also gone through the existing posts related to this issue without any success. In my project, I have three TypeScript files that require the same ...

TypeScript conditional return type: effective for single condition but not for multiple conditions

In my code, I have implemented a factory function that generates shapes based on a discriminated union of shape arguments. Here is an example: interface CircleArgs { type: "circle", radius: number }; interface SquareArgs { type: "square" ...

Issue with Angular 6 and Html Webpack Plugin

My project was recently updated from Angular 5 to Angular 6, causing some changes in the file structure. The webpack.config files are no longer present and the project now operates under CLI engage. However, I encountered an issue after the migration rela ...

Handling Errors in RXJS Angular - Utilizing .subscribe and Observable Strategy

For creating a new product using a backend API, the Angular frontend code needs to make a call to the API. I am interested in learning how to implement error handling with the use of .subscribe method. Currently, I am utilizing HTTPClient along with Observ ...

What is the best way to implement a late-binding clone method in TypeScript classes?

Creating a simple Cloneable interface for all my data classes in JavaScript is a straightforward task. However, when it comes to typing it properly in TypeScript, things get a bit more complex. Currently, I am putting together a solution like this: class ...

Apply a see-through overlay onto the YouTube player and prevent the use of the right-click function

.wrapper-noaction { position: absolute; margin-top: -558px; width: 100%; height: 100%; border: 1px solid red; } .video-stat { width: 94%; margin: 0 auto; } .player-control { background: rgba(0, 0, 0, 0.8); border: 1px ...