hope i can get some ideas here
I have a material table with a row of input fields
i would like to use left and right arrow key to move between cells instead of tab
at the moment i am able to use keyup.arrowleft
and right
in console to trigger event and move to the index next to it but i do not know how it can be done to move focus from one cell to the one next to it.
Any help would be appreciate thank you so much and below is my code https://stackblitz.com/edit/angular-ivy-g8qhrj?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.module.ts
HTML
<table mat-table [dataSource]="dataSource">
<ng-container [matColumnDef]="col.key" *ngFor="let col of columnsSchema">
<th mat-header-cell *matHeaderCellDef>
{{ col?.label }}
</th>
<td mat-cell *matCellDef="let element">
<mat-form-field (keyup.arrowright)="moveRight(col.key)" (keyup.arrowleft)="moveLeft(col.key)">
<input [type]="col?.type" matInput [(ngModel)]="element[col.key]" />
</mat-form-field>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
TS file
export interface SpecimenType {
Test1: string;
Test2: string;
Test3: string;
Test4: string;
}
const COLUMNS_SCHEMA = [
{
key: 'Test1',
type: 'number',
label: 'Test1'
},
{
key: 'Test2',
type: 'number',
label: 'Test2'
},
{
key: 'Test3',
type: 'number',
label: 'Test3'
},
{
key: 'Test4',
type: 'number',
label: 'Test4'
},
];
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular ' + VERSION.major;
constructor() {}
columnsSchema: any = COLUMNS_SCHEMA;
selectedSpecimanType!: SpecimenType;
displayedColumns: string[] = COLUMNS_SCHEMA.map(col => col.key);
dataSource = new MatTableDataSource<any>();
ngOnInit(): void {
this.dataSource.data = [{ Test1: 0, Test2: 0, Test3: 0, Test4: 0}];
}
moveRight(event: any) {
console.log('moveright', event);
const position = this.displayedColumns.findIndex(result => event === result);
console.log(this.displayedColumns[position + 1]);
}
moveLeft(event: any) {
console.log('moveleft', event);
const position = this.displayedColumns.findIndex(result => event === result);
console.log(this.displayedColumns[position - 1]);
}
}
To achieve that, you need to first add template variable to the input in the html. In this case, I use #myInput
. Then replaced moveRight(col.key)
with moveRight($event)
. I will leave it to you to do the same for the moveLeft
.
...
<td mat-cell *matCellDef="let element">
<mat-form-field
(keyup.arrowright)="moveRight($event)"
(keyup.arrowleft)="moveLeft(col.key)">
<input [type]="col?.type" matInput [(ngModel)]="element[col.key]" #myInput/>
</mat-form-field>
</td>
...
In the TS file, you will need to use @ViewChildren
to query all the template variable we tagged in the html.
//... import stuff here
@Component({
...
})
export class AppComponent {
@ViewChildren('myInput', {read: ELementRef})
private _inputList!: QueryList<ElementRef>;
...
public moveRight(event: any) {
// get the currentIndex
const currentIndex = this.inputList
.reduce((matchedIndex, input, index) => {
if(input.nativeElement === event.srcElement) {
matchedIndex = index;
}
return matchedIndex;
}, -1);
if(currentIndex === this.inputList.length -1) {
// move back to the first input or do nothing.
return;
}
// Focus the next right sibling input.
this.inputList.get(currentIndex + 1).nativeElement.focus();
}
}
I noticed that there is an error related to BrowserAnimationsModule
in your stackBlitz. I need to add BrowserAnimationsModule
to import in app.module.ts
.
@NgModule({
imports [
...
BrowserAnimationsModule,
....
]
...
})
export class AppModule {}