Michael Oryl
Michael Oryl

Reputation: 21642

How to get name of input field from Angular2 FormControl object?

I have an Angular 2 application that uses the ReactiveForms module to manage a form that uses a custom validator. The validator receives a FormControl object. I have a few input fields that could use the same custom validator if only I knew the name of the field when the FormControl 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);
          });
    });
  }

Upvotes: 45

Views: 69472

Answers (7)

Michael Ziluck
Michael Ziluck

Reputation: 655

A one-line variation of the accepted answer (also resolves the bug I mention in the comment).

getName(control: FormControl): string | null {
  return Object.entries(control.parent?.controls ?? []).find(([_, value]) => value === control)?.[0] ?? null;
}

Upvotes: 1

Taras Sokolovskyi
Taras Sokolovskyi

Reputation: 121

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;
}

Upvotes: 11

Chris
Chris

Reputation: 611

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;
}

Upvotes: 48

Steve Brush
Steve Brush

Reputation: 3181

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) => {
  // ...
});

Upvotes: 8

dabicho
dabicho

Reputation: 393

Not exactly what you want but you could dynamically create the validator like in some examples.

like

typeBasedValidator(controlName: string): ValidatorFn {
  return(control: AbstractControl): {[key: string]: any} => {
     // Your code using controlName to validate
     if(controlName == "something") { 
       doSomething(); 
     } else { 
       doSomethingElse(); 
     }
  }
}

Then use the validator when creating the form, passing the control name like

Upvotes: 0

Radim Köhler
Radim Köhler

Reputation: 123851

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 :

2.1.2 (2016-10-27)

But following this issue: feat(forms): make 'parent' a public property of 'AbstractControl'

And as already stated here

2.2.0-beta.0 (2016-10-20)

Features

  • forms: make 'parent' a public property of 'AbstractControl' (#11855) (445e592)
  • ...

we could later change the ["_parent"] into .parent

Upvotes: 29

Bazinga
Bazinga

Reputation: 11194

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.

Upvotes: 5

Related Questions