@hardikslk You can create the following files:
tabindex.directive.ts:
import { Directive, Input, HostListener, Renderer } from '@angular/core';
@Directive({
selector: '[myTabindex]'
})
export class TabindexDirective {
@Input('myTabindex') myTabindex: string;
constructor(private renderer: Renderer) { }
@HostListener('keydown', ['$event']) onInputChange(e) {
var code = e.keyCode || e.which;
if (code === 13) {
let next: HTMLElement = this.getMyNextFocusableElement(e.srcElement);
if (next) {
e.preventDefault();
this.renderer.invokeElementMethod(next, 'focus', []);
}
}
}
private getMyNextFocusableElement(elem: HTMLElement): HTMLElement {
let tabindex: number = parseInt(this.myTabindex || '0');
let next: HTMLElement = MyUtils.getNextFocusableElement(elem, 'myTabindex', tabindex);
return next;
}
}
const MyUtils = (() => {
const FOCUSABLES = ['input', 'select', 'textarea', 'button', 'object'];
const FOCUSABLES_SELECTOR = FOCUSABLES.join(',');
function getNextFocusableElement(elem: HTMLElement, attrName: string, tabindex: number): HTMLElement {
let form = getFormElement(elem);
let next = null;
tabindex++;
next = getElement(form, attrName, tabindex);
while (next) {
next = getFocusableElement(next);
if (next) {
return next;
}
tabindex++;
next = getElement(form, attrName, tabindex);
}
return null;
}
function getFormElement(elem: HTMLElement): HTMLElement {
let form: HTMLFormElement = elem ? (<HTMLInputElement>elem).form : null;
return form;
}
function getElement(form: HTMLElement, attrName: string, tabindex: number): HTMLElement {
let selector = `[${attrName}="${tabindex}"]`;
let elem = form ? <HTMLElement>form.querySelector(selector) : null;
return elem;
}
function getFocusableElement(elem: HTMLElement): HTMLElement {
let tagName = elem.tagName.toLowerCase();
let focusable = FOCUSABLES.some(
tagFocusable => tagFocusable === tagName
);
if (!focusable) {
elem = <HTMLElement>elem.querySelector(FOCUSABLES_SELECTOR);
focusable = !!elem;
}
if (focusable) {
//TODO: verify if elem disabled, readonly, hidden, etc...
// in which case focusable must be changed to false
}
if (focusable) {
return elem;
}
}
return {
getNextFocusableElement: getNextFocusableElement
};
})();
Remember to include it in your module:
import { TabindexDirective } from '../directives/tabindex.directive';
import { Page1 } from '../pages/page1/page1';
import { Page2 } from '../pages/page2/page2';
import { MyApp } from './app.component';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
@NgModule({
declarations: [
MyApp,
Page1,
Page2,
TabindexDirective
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
Page1,
Page2
],
providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}
Then just use it in any component that you want. For example:
page1.ts:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
@Component({
selector: 'page-page1',
templateUrl: 'page1.html'
})
export class Page1 {
public model: { email: string, password: string } = {
email: '',
password: ''
};
constructor() { }
public onLogin() {
//TODO do login stuff here...
console.log('login: ', this.model);
}
}
page1.html:
<ion-header>
<ion-navbar>
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Page One</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h3>Tabindex Example</h3>
<form (ngSubmit)="onLogin()" novalidate="novalidate" #myForm>
<ion-item>
<ion-input
name="email"
type="email"
[(ngModel)]="model.email"
#email
myTabindex="1"
></ion-input>
</ion-item>
<ion-item>
<ion-input
name="password"
type="password"
[(ngModel)]="model.password"
#password
myTabindex="2"
></ion-input>
</ion-item>
<button ion-button block>Login</button>
</form>
</ion-content>
Update
Like @KyleHoskins said, you can do this to make the directive significantly simpler:
tabindex.directive.ts:
import { Directive, HostListener, Input } from '@angular/core';
import { TextInput } from 'ionic-angular';
@Directive({
selector: '[myTabindex]'
})
export class TabindexDirective {
constructor(private inputRef: TextInput) { }
@HostListener('keydown', ['$event']) onInputChange(e) {
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
this.inputRef.focusNext();
}
}
}
page1.html:
<ion-header>
<ion-navbar>
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Page One</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h3>Tabindex Example</h3>
<form (ngSubmit)="onLogin()" novalidate="novalidate" #myForm>
<ion-item>
<ion-input
name="email"
type="email"
[(ngModel)]="model.email"
#email
myTabindex
></ion-input>
</ion-item>
<ion-item>
<ion-input
name="password"
type="password"
[(ngModel)]="model.password"
#password
></ion-input>
</ion-item>
<button ion-button block>Login</button>
</form>
</ion-content>
Take note that the number in front of the myTabindex
is unnecessary now, and I removed it from the last input otherwise it would return to the first, instead of triggering the submit action.
Also note that in this simplified way, TextInput
is from the ionic-angular
package and is related to an Ionic2
input, so you probably cannot use the directive in a custom component (you would receive some exception saying that there is no provider for TextInput
or something in these lines). There is also the fact that focusNext()
is labeled with @private
so I don’t know if it is a good idea to use it.
And that’s it! Hope it helps.