I\'ve got Angular2 reactive form. I created formControl
s and assigned it to input fields by[formControl]=...
. As I understand it creates nati
I can share one terrible solution but it works for me.
In reactive forms we can use either
1) FormControlDirective
ts
myControl = new FormControl('')
template
<input type="text" [formControl]="myControl">
or
2) FormControlName
ts
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.myForm = this.fb.group({
foo: ''
});
}
template
<form [formGroup]="myForm">
<input type="text" formControlName="foo">
</form>
So for these directives i could write some patch like
1) FormControlDirective
const originFormControlNgOnChanges = FormControlDirective.prototype.ngOnChanges;
FormControlDirective.prototype.ngOnChanges = function() {
this.form.nativeElement = this.valueAccessor._elementRef.nativeElement;
return originFormControlNgOnChanges.apply(this, arguments);
};
2) FormControlName
const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function() {
const result = originFormControlNameNgOnChanges.apply(this, arguments);
this.control.nativeElement = this.valueAccessor._elementRef.nativeElement;
return result;
};
After that we can easily access to native element having FormControl
instance
1) FormControlDirective
focusToFormControl() {
(<any>this.myControl).nativeElement.focus();
}
2) FormControlName
focusToFormControlName(name) {
(<any>this.myForm.get(name)).nativeElement.focus();
}
Plunker Example
Hello to whoever reads this! I hope you are having as much fun as I am playing with Angular :)
I spent a little time with a related issue that lead me here. The "injector" directive solution seems to me like a workaround, and an unpredictable way of approaching the problem.
I suspect the Angular team may have their reasons why this isn't "out-of-the-box." Perhaps they don't want to tie a FormControl to an exact DOM element since these two can outlive one another or get removed (in the case of the native element), or get cloned (which would not copy the "extended" assignment and, thus, effectively loosing any assignment from the directive targeting the original DOM element and a previous clone of the FormControl object), etc.
So after some thought, I went with this solution:
// Get the first invalid formControlName to scroll to:
const firstInvalidField =
Object.keys(form.controls).find(field => form.get(field)?.invalid);
// My DOM element query selector string
const selector = `[formControlName=${firstInvalidField}]`;
console.log(
document.querySelector( ${selector} )`
);
Now I'm guaranteed that I will have the reference to the DOM Element.
Added minor fix to baHI answer(moved logic to OnInit). Error mentioned in comments is probably connected to changes in forms. This answer is for "@angular/forms": "~7.1.0",
@Directive({
selector: '[ngModel]'
})
export class NativeElementInjectorDirective implements OnInit {
constructor (private el: ElementRef, private control : NgControl) {}
ngOnInit(){
(this.control.control as any).nativeElement = this.el.nativeElement;
}
}
Yes, you have to write Directive with [formControl], [formControlName]
selector.
Full example:
import { Directive, ElementRef } from "@angular/core";
import { NgControl } from '@angular/forms';
@Directive({
selector: '[formControl], [formControlName]'
})
export class ControlErrorsDirective {
get control() {
return this.controlDir.control;
}
constructor(
private controlDir: NgControl,
private host: ElementRef<HTMLFormElement>) {
}
ngOnInit() {
console.log(this.host.nativeElement);
}
}
and in your html just use formControlName
like this:<input formControlName='name' />
The code below does not work with pure ngModel binding, so I did a lot of experiments. Latest, also confirmed by Maximillian Schwarzmuller should be the one:
@Directive({
selector: '[ngModel], [formControl]', // or 'input, select, textarea' - but then your controls won't be handled and also checking for undefined would be necessary
})
export class NativeElementInjectorDirective {
constructor(private el: ElementRef, private control : NgControl, @Optional() private model : NgModel) {
if (!! model)
(<any>model.control).nativeElement = el.nativeElement;
else
(<any>control).nativeElement = el.nativeElement;
}
}
So if this directive is provided and exported in the main module, it will attach a custom nativeElement property to all FormControl.
It's a shame it's not coming out-of-the box...