18

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
  • 3,786
  • 2
  • 36
  • 56

1 Answers1

48

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.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 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. – XDS Oct 29 '16 at 21:32
  • 1
    What to you expect to get from `@ViewChild()` in this case? The element where the template variable is added (``), to or `ngForm` directive, that is assigned to the variable? – Günter Zöchbauer Oct 30 '16 at 16:02
  • 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 :) ) – XDS Oct 31 '16 at 09:17
  • 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 `` element instead. – Günter Zöchbauer Oct 31 '16 at 09:20
  • 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. – XDS Oct 31 '16 at 09:38
  • 1
    It's not just his point of view, this is how it was designed and makes sense to me. – David Barker Jan 17 '18 at 08:14