June 2, 2016

Ionic 2 set Focus of Input Element

Playing around with Ionic 2 i came to the following issue. I had a component where i’d like to automatically set the focus of an element after the view is loaded completely. The approach is similar to the AngularJS/Ionic1 one. We create a directive. But instead of using jQuery or jQuery Lite to set the focus the Angular 2 renderer is used.

First of all create a new directive with Ionic 2 CLI:

We end up with the following code generated in: app/components/focuser/focuser.ts

Now add the directive to your app.ts:

Lets add some functionality to focuser.ts:

Our focuser directive is now ready to use:

Show Keyboard for focused Input

Add the following line to show the devices keyboard.

Full Code:

Limitations – Scroll to the element

Since it is troubling to trigger the ion-content scroll event from outside of the components controller it is difficult to scroll to the focused element. So in order to get usable result, the input button should be in the visible view (with open keyboard).

If you want to scroll to the position you need to get the elements position: 

Then submit an event with the value to the view components class and use the scrollTo(x, y, duration) method provided by ion-content to scroll to the position.

Related Posts

Can Kattwinkel
Developer at thecodecampus </>


10 responses to “Ionic 2 set Focus of Input Element”

  1. david he says:

    Thank you very much for the tutorial. I’m working on a similar problem where I’m trying to focus the element after the keyboard pops up. I can access the keyboard event but for some reason nativeElement is undefined.

    I copied parts of your code and the main difference is, I’m not using a directive, so I’m getting the elementRef through the ViewChild.

    export class HomePage {

    @ViewChild(‘chat’) chat: any;
    @ViewChild(‘message’) message: ElementRef;

    window.addEventListener(‘native.keyboardshow’, this.keyboardShowHandler);

    }

    keyboardShowHandler(e:any){
    console.log(‘Keyboard height is: ‘ + e.keyboardHeight);
    console.log(‘message is: ‘, this.message);
    // this.message.nativeElement.focus();
    setTimeout(() => {
    this.renderer.invokeElementMethod(this.message.nativeElement, ‘focus’, []);
    }, 0);
    }
    }

    Any advice would be appreciated!

    Thanks

    • Thomas says:

      I’m trying with :

      @ViewChild(‘myInput’) myInput;

      constructor(private navCtrl: NavController, private renderer: Renderer) { }

      ionViewLoaded() {
      console.log(this.myInput); //working

      setTimeout(() => {
      this.renderer.invokeElementMethod(this.myInput, ‘focus’, []);
      //DOES’NT WORK : “el[methodName].apply is not a function”
      }, 350);
      }

      • Aline says:

        English:
        Thank you!
        You saved me, I needed to change the type of the password field for text on the page load. Here’s my solution:

        Portuguese:
        Obrigada,
        Você me salvou, precisava alterar o tipo do campo de senha para texto no carregamento da página. Segue a minha solução:

        @ViewChild(‘txtUsuario’) txtUsuario;
        constructor( private renderer:Renderer) { }

        ionViewDidLoad(){
        this.txtUsuario.type = “text”;
        }

  2. Can Kattwinkel says:

    Hey David,

    if the native element is undefined your code is probably running to early. Check if this.message is defined when you invoke renderer. To do so you could increase the timout (only for testing) or make use of the ng2 and ionic lifecycle hooks.

    The advantage of a attribute directive is that it can only run when the dom element is renderer.

  3. david he says:

    Hi Can,
    Thanks for the reply. I just checked this.message and it is indeed defined. When I console.log it, it prints the html markup for my input control.

    I just put this.message.nativeElement inside ngAfterViewInit and it is still null. I don’t know what I’m doing wrong.

  4. Can Kattwinkel says:

    Hey David,

    if console.log(this.message) prints out HTML markup than you already have the “nativeElement”. See this image of a correct implemented @ViewChild(“”) selector:

    http://blog.thecodecampus.de/wp-content/uploads/2016/07/nativeelement.png

    In the first line you can see the log of (this.message) and in the second one you see the log for (this.message.nativeElement).

    You can either put this.message directly into the renderer or fix your ViewChild decorator. Here is how my ViewChild was selected for the Screenshot. The matching is from the local variable #uiProgress in HTML to the ViewChild(“uiProgress”)

    HTML:

    export class ProgressBar {
    @ViewChild(“uiProgress”) uiProgress:ElementRef;

    }

    I hope this helps you!

  5. Navid says:

    Great and very useful. thanks.

  6. Klemens says:

    Very helpful, thanks.
    Just three things I had to change or add:

    1. I needed to add the [Focuser] @Directive to the page, not the MyApp class. Only with this it worked for me.

    2. I had to add a delay of 500 ms for the timeout, otherwise it would always trigger my form Validator that checks for empty input (also checks the ‘touched’ property).

    3. Added some code so that it works for textarea as well.

    ngAfterViewInit() {
    let element = this.elementRef.nativeElement.querySelector(‘input’);
    if (!element) {
    element = this.elementRef.nativeElement.querySelector(‘textarea’);
    }
    // we need to delay our call in order to work with ionic …
    setTimeout(() => {
    this.renderer.invokeElementMethod(element, ‘focus’, []);
    Keyboard.show();
    }, 500); // with 0 it will trigger a validation error instantly
    }

  7. valter says:

    Hi there great post, althougth you can fix a small issue:

    Keyboard.show(); does not work with IOS, instead add this in the config.xml

    See http://mhartington.io/post/setting-input-focus/

    Great job,
    Valter

Leave a Reply

Add code to your comment in Markdown syntax.
Like this:
`inline example`

```
code block
example
```

Your email address will not be published.