10

I've many input boxes in a div and I need to focus one of them programmatically.

How to do it?

It's something like:

<div>
<input type="text" name="txt1" />
<input type="text" name="txt2" />
<input type="text" name="txt3" />
<input type="text" name="txt4" />
<input type="text" name="txt5" />
<input type="text" name="txt6" />
</div>
<button (click)="selectSample()" />

selectSample() {
    ?????????('txt3').focus();
    console.log('Button pressed, txt3 has been selected');
}
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Marco Jr
  • 6,496
  • 11
  • 47
  • 86
  • try to take reference using java script like `document.getElementByid('')` or post some code to make it more clear – Pardeep Jain Mar 31 '16 at 09:43
  • 1
    Looks like http://stackoverflow.com/questions/34522306/angular-2-focus-on-newly-added-input-element/34573219#34573219 – Günter Zöchbauer Mar 31 '16 at 09:52
  • Check also my answer [here](https://stackoverflow.com/a/42745350/3170087). It isn't dependent on component where you want to use it. – Makla Sep 06 '17 at 08:58

4 Answers4

11
@Component({
  selector: 'my-app',
  template: `
<div>
<input #input type="text" name="txt1" />
<input #input type="text" name="txt2" />
<input #input type="text" name="txt3" />
<input #input type="text" name="txt4" />
<input #input type="text" name="txt5" />
<input #input type="text" name="txt6" />
</div>
<button (click)="selectSample()">click</button>
`
})
export class App {
  @ViewChildren('input') inputs;

  selectSample() {
    // console.debug(this.inputs.toArray().find((e) => {
    //  return e.nativeElement.getAttribute('name') == 'txt3';
    //}).nativeElement.value);

    this.inputs.toArray().find((e) => {
      return e.nativeElement.getAttribute('name') == 'txt3';
    }).nativeElement.focus();

  }
}

Plunker example

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
8

Take a look at the ViewChild (and ViewChildren like Gunter suggests) annotations.

You can do something like this:

@Component({
  selector: 'samples',
  template: `
    <div>
      <input type="text" name="txt1">
      <input type="text" name="txt2">
      <input type="text" name="txt3" #input3>
      <input type="text" name="txt4">
      <input type="text" name="txt5">
      <input type="text" name="txt6">
    </div>
    <button (click)="selectSample()">select</button>`
})    
export class SamplesComponent {

  @ViewChild('input3') input3:ElementRef;

  constructor(private _renderer : Renderer) {}

  public selectSample() {
     //as per Eric's suggestion
     this._renderer.invokeElementMethod(this.input3.nativeElement, 'focus', []);
  }
}
Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • @EricMartinez Ohh, would you look at that! I suppose you are talking about `Renderer` in combination with `invokeElementMethod`? Just looked at the documentation, never heard of `Renderer` before. Thanks for this idea. I somehow dislike the idea as well of using `nativeElement` – Poul Kruijt Mar 31 '16 at 13:17
  • yes, actually accessing directly nativeElement is discouraged, see [ElementRef](https://angular.io/docs/ts/latest/api/core/ElementRef-class.html), so the safest way is use Renderer since is webworker safe. – Eric Martinez Mar 31 '16 at 13:47
  • 1
    @EricMartinez I've updated my answer. Thanks for the explanation, it especially makes sense now when you take webworkers into consideration. – Poul Kruijt Mar 31 '16 at 13:56
  • Renderer is deprecated and new [Renderer2 doesn't have invokeElementMethod](https://github.com/angular/angular/issues/13818#issuecomment-283417820) method. – Makla Sep 06 '17 at 08:35
4

Here's a Directive implementation, per Eric's suggestion:

@Directive({selector: 'input'})
export class MyInput {
  constructor(private _elRef:ElementRef, private _renderer:Renderer) {}
  focusIf(attrValue:string) {
    console.log(this._elRef.nativeElement.getAttribute('name'))
    if(this._elRef.nativeElement.getAttribute('name') === attrValue) {
      this._renderer.invokeElementMethod(this._elRef.nativeElement, 'focus', []);
      return true;
    }
    return false;
  }
}

@Component({
  selector: 'my-app',
  directives: [MyInput],
  template: `<div>
      <input type="text" name="txt1">
      <input type="text" name="txt2">
      <input type="text" name="txt3">
      <input type="text" name="txt4">
      <input type="text" name="txt5">
      <input type="text" name="txt6">
    </div>
    <button (click)="selectSample()">click</button>`
})
export class AppComponent {
  @ViewChildren(MyInput) inputs;
  constructor() { console.clear(); }
  selectSample() {
    this.inputs.toArray().some(myInput =>
      myInput.focusIf('txt3'));
  }
}

Plunker

I like the directive approach, because we don't need to add #input to the HTML, and the directive knows how to focus itself.

I used Array.some() just to be slightly more efficient.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Renderer is deprecated and new [Renderer2 doesn't have invokeElementMethod](https://github.com/angular/angular/issues/13818#issuecomment-283417820) method. – Makla Sep 06 '17 at 08:35
3

My approach would be to rely on a Directive and its selector, to remove the necessity of iterating over an array, and to avoid the local variables (I don't like the idea of having too many of them on my view), as Mark mentioned.

I'm assuming that the user always want only one and it's hardcoded, which is enough for the case the user asked about. Having a dynamic element to focus would defeat this approach.

The directive

@Directive({
  selector : 'input[type=text][name=txt3]'
})
class Input {
  constructor(public renderer: Renderer, public elementRef: ElementRef) {}

  focusMe() {
    this.renderer.invokeElementMethod(this.elementRef.nativeElement, 'focus', []);
  }
}

The component would be basically the same in each answer

export class App {
  @ViewChild(Input) input: Input;

  selectSample() {
    this.input.focusMe();
  }
}

Here's the plnkr.

ljgw
  • 2,751
  • 1
  • 20
  • 39
Eric Martinez
  • 31,277
  • 9
  • 92
  • 91
  • Renderer is deprecated and new [Renderer2 doesn't have invokeElementMethod](https://github.com/angular/angular/issues/13818#issuecomment-283417820) method. – Makla Sep 06 '17 at 08:34