How to get name of input field from Angular2 FormControl object?
Solution 1
To expand on Radim Köhler answer. here is a shorter way of writing that function.
getControlName(c: AbstractControl): string | null {
const formGroup = c.parent.controls;
return Object.keys(formGroup).find(name => c === formGroup[name]) || null;
}
Solution 2
We can use .parent
property, well today ["_parent"]
(see more below):
export const getControlName = (control: ng.forms.AbstractControl) =>
{
var controlName = null;
var parent = control["_parent"];
// only such parent, which is FormGroup, has a dictionary
// with control-names as a key and a form-control as a value
if (parent instanceof ng.forms.FormGroup)
{
// now we will iterate those keys (i.e. names of controls)
Object.keys(parent.controls).forEach((name) =>
{
// and compare the passed control and
// a child control of a parent - with provided name (we iterate them all)
if (control === parent.controls[name])
{
// both are same: control passed to Validator
// and this child - are the same references
controlName = name;
}
});
}
// we either found a name or simply return null
return controlName;
}
and now we are ready to adjust our validator definition
public asyncValidator(control: FormControl): {[key: string]: any} {
//var theFieldName = control.someMethodOfGettingTheName(); // this is the missing piece
var theFieldName = getControlName(control);
...
.parent
later, ["_parent"]
now
At the moment (today, now), current release is :
But following this issue: feat(forms): make 'parent' a public property of 'AbstractControl'
And as already stated here
Features
- forms: make 'parent' a public property of 'AbstractControl' (#11855) (445e592)
- ...
we could later change the ["_parent"]
into .parent
Solution 3
As of Angular 4.2.x, you can access a FormControl
's parent FormGroup
(and its controls) using the public parent property:
private formControl: FormControl;
//...
Object.keys(this.formControl.parent.controls).forEach((key: string) => {
// ...
});
Solution 4
You have two options:
With the help of the Attribute
decorator:
constructor(@Attribute('formControlName') public formControlName) {}
With the help of the Input
decorator:
@Input() formControlName;
To use this your validation needs to be a directive of course.
Solution 5
You can set control name in validators:
this.form = this.fb.group({
controlName: ['',
[
Validators.required,
(c) => this.validate(c, 'controlName')
]
]
});
And then:
validate(c: FormControl, name) {
return name === 'controlName' ? {invalid: true} : null;
}
Michael Oryl
I'm the Director of Web Development for a life insurance company. In my previous life I was the founder and editor-in-chief of MobileBurn.com. For work I focus mostly on JavaScript projects using Node.js, Angular, and Node. I also dabble in iOS/Swift when needed, though I claim no proficiency there. I used to spend a lot of time with Java/Groovy, but I don't work in that space any longer. For fun, I like working on Arduino and similar micro-controllers for robotics projects as well as generic "maker" stuff. #SOreadytohelp
Updated on February 16, 2022Comments
-
Michael Oryl over 2 years
I have an Angular 2 application that uses the
ReactiveForms
module to manage a form that uses a custom validator. The validator receives aFormControl
object. I have a few input fields that could use the same custom validator if only I knew the name of the field when theFormControl
was passed to the validator.I can't find any method or public property on
FormControl
that exposes the input field's name. It's simple enough to see its value, of course. The following shows how I would like to use it:public asyncValidator(control: FormControl): {[key: string]: any} { var theFieldName = control.someMethodOfGettingTheName(); // this is the missing piece return new Promise(resolve => { this.myService.getValidation(theFieldName, control.value) .subscribe( data => { console.log('Validation success:', data); resolve(null); }, err => { console.log('Validation failure:', err); resolve(err._body); }); }); }
-
Michael Oryl over 7 yearsThis certainly lets one gain access to the form, but how do you then identify which of the (many) fields in the form is represented by your original
FormControl
that was passed into the validator? -
Radim Köhler over 7 yearsI am not fully sure about your concern. The point is: The
name
of aFormControl
is given only by its parentFormGroup
. Because only such parent has a MAP ofcontrols
and the keys, represent the name. And because every control has the information, who is its.parent
, we can get that information anywhere.. even inside of validator .. just by calling the above (or adjusted/optimized) functiongetControlName(controlPassedToValidtor)
. Other words, even single control, passed to validator function is enough - to get its name. Only condition is: it must be child ofFormGroup
-
Michael Oryl over 7 yearsThe concern is access to the FormGroup gives me access to every FormControl it contains along with the name of every field they represent. But out of that list of FormControls I now have access to, how do I know which one is the FormControl that was passed into the validator. I need the name of a specific field. Unless there's something I'm missing, what you are providing is a list of all fields....
-
Radim Köhler over 7 yearsHmmm how to explain it. We have a control inside of the validator. We have a reference to it. We also have a dictionary a set of
{ Name1: control1, Name2: control2, ...}
. Each control (1, 2, ...) is an instance of some FormControl (or other FormGroup or FormArray). So, we can compare the control passed to the validator with each of them. If the reference is the samecontrol === parent.controls[name]
we got a match. Such parent child is the same as the control passed to validator. Helping? or ... ? I mean, we have to have a control, which we do.. it was passed... -
Radim Köhler over 7 yearsI extended my answer, with more details in comments, and mostly - showed how inside of the validator we can call that function, and receive a name of that control... hope now it is clear
-
Michael Oryl over 7 yearsOK, I will give that a shot. Thanks.
-
gerleim over 6 yearsStill (as of Aug of 2017) no better way than to iterate through all the controls?
-
FireDragon over 6 yearsmost succinct answer, but it's still clear what's going on. works for me in Angular 4. thanks
-
ShadSterling almost 6 yearsWouldn't it be better to construct a Map of AbstractControls to names and look up in that, rather than generating the key list and then searching the key list for each lookup?
-
Mr AH over 5 yearsyou just saved me from a lot of pain... I had a async validator in reactiveforms which used the control name to go off to a service and get some validation information back. but it was always called initially on the new FormControl bit, so the parent was undefined, and if you return of(null) to get past the undefined parent, it will always set the control to VAILD even though there are errors on the next call!
-
lonix almost 5 yearsDoesn't work:
"Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ [key: string]: AbstractControl; } | AbstractControl[]'."
-
Nowdeen almost 4 yearsHere, in may 2020, I had to change this.formControl.parent.controls by this.formControl.value in the above suggested code.
-
kvetis almost 4 yearsThis is very resource expensive operation. Imagine each control in form iterating over all components in form. Complexity of rendering form is O(n²). But doing this only occasionaly on single field is okay.
-
321X almost 4 years@Ionix: Use this:
Object.keys(c.parent.controls).find(name => c === c.parent.get(name))
-
Shailesh Bhat about 3 years
const formGroup = c["_parent"].controls;
return Object.keys(formGroup).find(name => c === formGroup[name]) || null;
this code worked for me @4.1.5 version of typescript. Works for all versions may be..But, I'm not sure -
Michael Ziluck over 2 yearsThis answer is wrong. If the name of the form control is an empty string, it will return
null
rather than the empty string due to the way the||
operator works in JavaScript. You must use??
instead.