Using @ViewChild to get hold of the .nativeElement of an input returns 'undefined'

37,850

Just add another template variable

#foobar="ngForm" #foobarElement
@ViewChild("foobarElement") foobar: ElementRef;

With ="ngForm" the template variable gets a different purpose and doesn't work with @ViewChild()

If you think this should still work, consider creating a bug report.

Share:
37,850
XDS
Author by

XDS

Updated on July 23, 2022

Comments

  • XDS
    XDS almost 2 years

    I created the following form in angular2:

    import {Component, ElementRef, ViewChild, AfterViewInit} from "angular2/core";
    import {Observable} from "rxjs/Rx";
    import {ControlGroup, Control, Validators, FormBuilder} from "angular2/common";
    
    @Component({
        selector: "ping-pong",
        template: `
                      <form
                         name="someform"                             [ngFormModel]="form">
                          <div class="form-group">
                              <input                                 
                                  id="foobar"                        #foobar="ngForm"   <-- without ="ngForm" everything works fine
                                  type="text"                        ngControl="foobar"
                                  value=""
                                  class="form-control"
                              />
                          </div>
                      </form>
                  `,
        styles: [`
                      form {
                          width: 300px;
                      }
                  `]
    })
    
    export class ChangepswdFormComponent implements AfterViewInit {
        @ViewChild("foobar") foobar: ElementRef;
        private form: ControlGroup;
    
        public constructor(formBuilder: FormBuilder) {
            this.form = formBuilder.group({
                foobar: [""]
            });
        }
    
        public ngAfterViewInit(): void {
            console.log(this.foobar.nativeElement);
            //observable doesnt work because nativeelement is undefined
            //Observable.fromEvent(this.foobar.nativeElement, "keyup").subscribe(data => console.log(data));
        }
    }
    

    Inside ngAfterViewInit the nativeElement bit is undefined unless I remove the = "ngForm" part from #foobar = "ngForm".I overcame this problem by making the following tweaks:

    import {Component, ElementRef, ViewChild, AfterViewInit} from "angular2/core";
    import {Observable} from "rxjs/Rx";
    import {ControlGroup, Control, Validators, FormBuilder} from "angular2/common";
    
    @Component({
        selector: "ping-pong",
        template: `
                      <form
                         name="someform"                             [ngFormModel]="form">
                          <div class="form-group">
                              <input                                 
                                  id="foobar"                        #foobar="ngForm"     #tralala
                                  type="text"                        ngControl="foobar"
                                  value=""
                                  class="form-control"
                              />
                          </div>
                      </form>
                  `,
        styles: [`
                      form {
                          width: 300px;
                      }
                  `]
    })
    
    export class ChangepswdFormComponent implements AfterViewInit {
        @ViewChild("tralala") foobar: ElementRef;
        private form: ControlGroup;
    
        public constructor(formBuilder: FormBuilder) {
            this.form = formBuilder.group({
                foobar: [""]
            });
        }
    
        public ngAfterViewInit(): void {
            console.log(this.foobar.nativeElement);
            let keyups = Observable.fromEvent(this.foobar.nativeElement, "keyup");
            keyups.subscribe(data => console.log(data));
        }
    }
    

    In other words I enriched the input element with an auxilary hashtag (#tralala) which is not related to ngForm.Even though this solution works I don't feel at ease with it because it feels like I'm applying a small hack. We should somehow be able to retrieve the native element of the textbox in a more elegant/straightforward way either through @ViewChild or through this.form.controls (or something to that effect) WITHOUT resorting to workarounds like the one I used. But I can't figure out exactly how.

    Quick Addendum: I'm using Angular2 2.0-beta7 if that's of any importance.

  • XDS
    XDS over 7 years
    Thanks for tuning in. As I already mentioned I tried this approach and it worked indeed. However (as have also already stated) I find the approach a bit ... counter-intuitive.
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    What to you expect to get from @ViewChild() in this case? The element where the template variable is added (<input>), to or ngForm directive, that is assigned to the variable?
  • XDS
    XDS over 7 years
    I have update the wording to emphasize my goal: "We should somehow be able to retrieve the native element of the textbox in a more elegant/straightforward way either through @ViewChild or through this.form.controls (or something to that effect) WITHOUT resorting to workarounds like the one I used." <- I'm refering to the auxilary-tag workaround I used - just because it works it doesn't mean it's elegant or the "canon" way of doing things in Angular2 (yeah I know I'm a perfectionist :) )
  • Günter Zöchbauer
    Günter Zöchbauer over 7 years
    No idea why you think it's a workaround. This is how you get a reference to an element in Angular2. You assign the NgForm directive to the template variable, therefore it doesn't seem right to me that @ViewChild() should return the <input> element instead.
  • XDS
    XDS over 7 years
    Point taken. I'm still having some doubts though because I would like more people to confirm your point of view before I adopt it.
  • David Barker
    David Barker over 6 years
    It's not just his point of view, this is how it was designed and makes sense to me.