'Angular: access FormControl from Directive
I would like to add validators dynamically to my FormControl via 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 don't need to reference the nativeElement (via ElementRef).
I would like to reference the formControl...
...and use it as such:
// HTML with my custom directive 'idNumber' ////////
<custom-input-string
idNumber
[name]="'idNumber'"
[label]="Id Number"
[placeholder]="">
</custom-input-string>
// TS ////////
@ViewChild(CustomInputStringComponent) child: CustomInputStringComponent;
ngAfterViewInit() {
setTimeout(() => {
this.child.insertIntoForm(this.signupForm);
}, 0);
}
Any ideas?
Thank you all.
Solution 1:[1]
Here is an example of using a directive to append validators to your form control.
Note that using that will result in losing all of your previous validators.
constructor(
// Get the control directive
private control: NgControl
) { }
ngOnInit() {
const abstractControl = this.control.control;
abstractControl && abstractControl.setValidators([Validators.required]);
}
Solution 2:[2]
If you utilize NgControl and constructor DI injection we can have a directive applicable to form controls from reactive forms in either formControlName or template driven forms:
Directive:
import { Directive } from "@angular/core";
import { NgControl } from "@angular/forms";
@Directive({
selector: '[my-directive]'
})
export class MyDirective {
constructor(private el: ElementRef, private control : NgControl) { }
}
Solution 3:[3]
The easiest and 'cleanest' way is to use 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 {
// your validation here
return null
}
}
Solution 4:[4]
//TestAnythingsComponent.ts
import { Component, OnInit } from '@angular/core';
import { FormControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { IdNumberDirective } from '../directives/IdNumberDirective.directive';
@Component({
selector: 'app-test-anythings',
templateUrl: './test-anythings.component.html',
styleUrls: ['./test-anythings.component.css'],
providers:[IdNumberDirective]
})
export class TestAnythingsComponent implements OnInit {
testForm: FormGroup;
constructor(fb: FormBuilder, IdNumberDirective : IdNumberDirective) {
this.testForm = fb.group({
idNumberFormControl : new FormControl(null,
Validators.compose([
Validators.required,
Validators.minLength(3),
Validators.maxLength(8),
IdNumberDirective.customValidator()
])
),
})
}
}
//IdNumberDirective.ts
import { Directive, OnInit } from '@angular/core';
import { Validators, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
@Directive({
selector: '[idNumber]'
})
export class IdNumberDirective implements OnInit {
constructor() {
}
ngOnInit() {
}
customValidator(): ValidatorFn {
Validators.nullValidator
return (control: AbstractControl): ValidationErrors | null => {
//any condition to check control value
if (control.value != "Sachin") {
//return key value pair of errors
return { "customError": { inValid: true, errMsg: 'Invalid Value' } };
}
return null;
}
}
}
//test-anythings.component.html
<form [formGroup]="testForm">
<input idNumber formControlName="idNumberFormControl" />
<div *ngIf="testForm.get('idNumberFormControl').invalid && testForm.get('idNumberFormControl').errors.customError.inValid"
style="color:red">
{{testForm.get('idNumberFormControl').errors.customError.errMsg}}
</div>
<button type="submit">submit</button>
</form>
Solution 5:[5]
You can access both FormGroup and FormControl in your directive by FormGroupDirective:
NOTE: I'm working on a country selection in this example.
import { FormGroupDirective } from "@angular/forms";
Then:
{
constructor(private fg: FormGroupDirective) { }
// Access the FormGroup
console.log('My FormGroup values: ', this.fg.value);
// Access the FormControl
console.log('The selectedCountryCtrl: ', this.fg.control.controls.selectedCountryCtrl);
console.log('The selectedCountryCtrl value: ', this.fg.control.controls.selectedCountryCtrl.value);
// Access the variable/object directly
console.log('My FormControl selectedCountry value: ', this.fg.value.selectedCountry);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Vahid |
| Solution 2 | Suresh Kumar Ariya |
| Solution 3 | mumenthalers |
| Solution 4 | |
| Solution 5 | Dharman |
