Is it possible to get native element for formControl?

前端 未结 5 476
面向向阳花
面向向阳花 2020-12-03 09:36

I\'ve got Angular2 reactive form. I created formControls and assigned it to input fields by[formControl]=.... As I understand it creates nati

相关标签:
5条回答
  • 2020-12-03 10:02

    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

    0 讨论(0)
  • 2020-12-03 10:03

    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.

    0 讨论(0)
  • 2020-12-03 10:11

    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;
            }
        }
    
    0 讨论(0)
  • 2020-12-03 10:27

    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' />

    0 讨论(0)
  • 2020-12-03 10:28

    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...

    0 讨论(0)
提交回复
热议问题