3

I have a custom form component that implements ControlValueAccessor. This component has an internal property touched.

export class BmInputComponent implements ControlValueAccessor, Validator {

    private onTouchedCallback: () => {};
    private touched: boolean = false;

    registerOnTouched(fn: any) {
        this.onTouchedCallback = fn;
    }

    onBlur() {
        this.touched = true;
        this.onTouchedCallback();
    }
}

I need to implement a method like

markTouched() {
    this.touched = true;
}

That could be called by the user of the component when markAsTouched is executed in the formControl: customInputControl.markAsTouched()

I cannot find an angular-way to do this.

@Edit: Tried to inject the NgControl:

@Component({
    selector: 'bm-input',
    templateUrl: './bm-input.component.html',
    encapsulation: ViewEncapsulation.None,
    providers: [
         {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => BmInputComponent),
            multi: true
        }
    ]
})
export class BmInputComponent implements ControlValueAccessor, Validator {

    private onTouchedCallback: () => {};
    private touched: boolean = false;

    constructor(@Self() @Optional() public _formControl: NgControl) {
        this._viewDate = new Date();
        if (this._formControl) {
            this._formControl.valueAccessor = this;
            this._formControl.statusChanges.subscribe(this.markTouched);
        }
    }
    registerOnTouched(fn: any) {
        this.onTouchedCallback = fn;
    }

    onBlur() {
        this.touched = true;
        this.onTouchedCallback();
    }

    markTouched() {
        if(this._formControl.touched)
            this.touched = true;
    }

}

But I am getting Cannot instantiate cyclic dependency! NgControl when the component is invoked with a formControl.

Zucca
  • 561
  • 1
  • 7
  • 21

3 Answers3

1

Have you tried @SkipSelf() instead of @Self()?

user1678153
  • 83
  • 1
  • 6
0

You could try this:

constructor(private injector: Injector) {
}


ngDoCheck() {

    let ngControl = this.injector.get(NgControl);
    if (! ngControl.control) {
        return;
    }

    this.touched = ngControl.control.touched;

}


pesche666
  • 139
  • 1
  • 13
0

The circular dependency is caused by having both the NG_VALUE_ACCESSOR in your @Component(...) providers, and injecting NgControl in the constructor. These are mutually exclusive.

See the example in the NG material documentation here: https://material.angular.io/guide/creating-a-custom-form-field-control#ngcontrol

w5l
  • 5,341
  • 1
  • 25
  • 43