Get a hold of a FormControl instance by querying a component's template
short answer: You can't
You just can't. (Well, maybe be you can but it will be hacky !)
long answer: you can't, but...
FormControl
is not injectable. Directives are injectable, but, you would have to deal with formControlName
,ngModel
,formControl
, etc and they wouldn't be accessible from the wrapping component but its children...
For your case you could try with @ContentChildren(FormControlName) theControl: any;
as there is no FormControlDirective
implied in your code, but you wouldn't be able to access the FormControl
anyway (property _control
is internal, soo it would be a hack)...
So you should stick to managing your errors from the component dealing with the FormGroup
.
BUT if you want to display a custom input (that won't display error message as is but will be able to show this input is in error state (the host element will get the ng-valid
, ng-invalid
classes, so it's just a matter of style), you can do this by implementing ControlValueAccessor
.
A bridge between a control and a native element.
A ControlValueAccessor abstracts the operations of writing a new value to a DOM element representing an input control.
It means directives/components implementing this interface can be used with ngModel
, formControl
,etc...
eg: <my-component [(ngModel)]="foo"></my-component>
it is not the exact reproduction of your problem, but this implementation solved the same kind of problem for me :
export const INPUT_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputComponent),
multi: true
};
@Component({
selector: "field",
template: `<!--put anything you want in your template-->
<label>{{label}}</label>
<input #input (input)="onChange($event.target.value)" (blur)="onTouched()" type="text">`,
styles: [],
providers: [INPUT_VALUE_ACCESSOR]
})
export class InputComponent implements ControlValueAccessor {
@ViewChild("input")
input: ElementRef;
@Input()
label:string;
onChange = (_: any) => { };
onTouched = () => { };
constructor(private _renderer: Renderer) { }
writeValue(value: any): void {
const normalizedValue = value == null ? "" : value;
this._renderer.setElementProperty(this.input.nativeElement, "value", normalizedValue);
}
registerOnChange(fn: (_: any) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
setDisabledState(isDisabled: boolean): void {
this._renderer.setElementProperty(this.input.nativeElement, "disabled", isDisabled);
}
}
then you can just :
<field label="Title" formControlName="title"></field>
You can get ahold of the Form Control Name instance via:
@Component({
selector: 'field',
templateUrl: './field.component.html',
styleUrls: ['./field.component.scss']
})
export class FieldComponent implements AfterContentInit {
@Input()
public label: string;
@ContentChild(FormControlName)
public controlName: FormControlName;
public ngAfterContentInit(): void {
console.log(this.controlName.control);
}
}