33

I've the following component:

@Component({
    selector: 'pc-radio-button',
    templateUrl: './radio-button.component.html',
    providers: [
        {provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FieldRadioButtonComponent), multi: true}
    ]
})
export class RadioButtonComponent implements ControlValueAccessor {
    ...
}

I can assign and alter the value through these inputs:

<pc-radio-button [formControl]="formControl"></pc-radio-button>
<pc-radio-button [formControlName]="inputControlName"></pc-radio-button>

However I need the component to have the direct access to the assigned formControl, as I need to add styles depending on its status.

By creating an @Input() formControl does not solve the problem. As it does not cover the case when form control is assigned via formControlName.

yurzui
  • 205,937
  • 32
  • 433
  • 399
Gediminas Bublys
  • 403
  • 1
  • 4
  • 10

3 Answers3

70

It looks like injector.get(NgControl) is throwing a deprecation warning, so I wanted to chime in with another solution:

constructor(public ngControl: NgControl) {
  ngControl.valueAccessor = this;
}

The trick is to also remove NG_VALUE_ACCESSOR from the providers array otherwise you get a circular dependency.

More information about this is in this talk by Kara Erickson of the Angular team.

Jesse
  • 2,391
  • 14
  • 15
  • 7
    This should be the accepted solution in 2019 with Angular 7. – AsGoodAsItGets May 14 '19 at 09:35
  • 1
    Can you please show what's the deprecation warning? – yurzui Jun 10 '19 at 11:30
  • 5
    FWIW, her presentation is totally worth your time. But if you are looking for the specific `NgControl` bits it starts around [19:07](https://youtu.be/CD_t3m2WMM8?t=1147) – Eric Liprandi Aug 21 '20 at 17:04
  • To add to this answer: there is no need to remove NG_VALUE_ACCESSOR from providers, or to add ngControl.valueAccessor = this; You can add NG_VALUE_ACCESSOR to the providers like this: ``` lang-js { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MyComponent), multi: true, }, ``` then you can just inject NgControl in the constructor – Remco Vlierman Jan 13 '21 at 12:03
  • 1
    Thank you @EricLiprandi, that was super useful. – Leandro Galluppi Feb 03 '21 at 21:48
  • 5
    @RemcoVlierman I get `Circular dependency in DI detected` when I do that – Ties Oct 03 '21 at 21:33
  • @Ties this forwardRef should prevent that, but maybe this behaviour has changed in a later release. But it also could be your setup, would have to look into that before I can tell ;-) – Remco Vlierman Oct 05 '21 at 07:51
28

One possible solution is to get NgControl instance via Injector:

import { NgControl } from '@angular/forms';
export class PasswordComponent implements ControlValueAccessor, AfterViewInit {
  ...
  ngControl: NgControl;

  constructor(private inj: Injector) {
    ...
  }

  ngAfterViewInit() {
    this.ngControl = this.inj.get(NgControl)
  }

then you can get status like

ngControl.control.status

See also

Gabriel Llamas
  • 18,244
  • 26
  • 87
  • 112
yurzui
  • 205,937
  • 32
  • 433
  • 399
2

Goes from my answer of this stackblitz question

Another solution is add as provider NG_VALIDATORS. So, in our function validate we can store the control in a variable

public validate(c: FormControl) {
  if (!this.control)
    this.control=c;
  return null;

See stackblitz

Eliseo
  • 50,109
  • 4
  • 29
  • 67