Issue
Here when i press the arrow keys left and right its not focusing or i cannot navigate or choose these button using my arrow keys, but when consoling the values of these keycode its getting printed according to the key pressed,I need to know how to gain focus on the html when pressing the arrow keys so that the buttons can be navigated through using arrow keys,It would be a great help if someone can provide me with a solution.
component.html
<ng-template #content let-modal>
<div class="modal-header">
<h3 class="modal-title">Accept Offer</h3>
</div>
<div class="modal-body">
<p>Do you really want to accept the offer?</p>
</div>
<div class="modal-footer>
<button type="submit" class="btn btn-primary tab" (click)="onAcceptOffer()" arrow-div>Submit</button>
<button type="button" class="btn btn-secondary tab" (click)="modal.dismiss('Crossclick');
isClicked=false" arrow-div>Cancel</button></div>
</ng-template>
Services.ts
export class KeyBoardService {
keyBoard:Subject<any>=new Subject<any>();
sendMessage(message:any)
{
this.keyBoard.next(message)
}
}
directive.ts
@Directive({
selector: '[arrow-div]',
})
export class ArrowDivDirective {
constructor(private keyboardService: KeyBoardService, public element: ElementRef, private render: Renderer2) {
this.render.setAttribute(this.element.nativeElement, "tabindex", "0")
}
@HostListener('keydown', ['$event']) onKeyUp(e) {
switch (e.keyCode) {
case 38:
this.keyboardService.sendMessage({ element: this.element, action: 'UP' })
break;
case 37:
this.keyboardService.sendMessage({ element: this.element, action: 'LEFT' })
break;
case 40:
this.keyboardService.sendMessage({ element: this.element, action: 'DOWN' })
break;
case 39:
this.keyboardService.sendMessage({ element: this.element, action: 'RIGTH' })
break;
}
}
}
component.ts
columns:number=2;
@ViewChildren(ArrowDivDirective) inputs:QueryList<ArrowDivDirective>
constructor(private keyboardService:KeyBoardService){}
ngOnInit()
{
this.keyboardService.keyBoard.subscribe(res=>{
this.move(res)
})
}
move(object)
{
const inputToArray=this.inputs.toArray()
let index=inputToArray.findIndex(x=>x.element==object.element);
switch (object.action)
{
case "UP":
index-=this.columns;
break;
case "DOWN":
index+=this.columns;
break;
case "LEFT":
index--;
break;
case "RIGTH":
index++;
break;
case "RIGTH":
index++;
break;
}
if (index>=0 && index<this.inputs.length)
{
inputToArray[index].element.nativeElement.focus();
}
}
Solution
I imagine the code goes from this SO
The "problem" with this approach is that you should focus one HTMLelement before you can use the arrows keys.
We can take another approach: the service subscribe to document.fromEvent('document')
Imagine a service like
import {map,filter,tap} from 'rxjs/operators'
@Injectable({
providedIn: 'root',
})
export class KeyBoardService {
keyBoard:Subject<any>=new Subject<any>();
subscription:Subscription
sendMessage(message:any)
{
this.keyBoard.next(message)
}
init()
{
if (!this.subscription)
this.subscription=fromEvent(document,'keydown').pipe(
filter((e:Event)=>{
const el=document.activeElement
const ev=(e as KeyboardEvent)
//only want if the key is an arrow
const isArrow=ev.keyCode>=37 && ev.keyCode<=40
//and is the "body" or is arrow down and we are not
//in a select or is an "arrow-div"
return isArrow && (el.tagName== 'body' ||
(ev.keyCode==40 && el.tagName!='SELECT') ||
el.getAttribute('arrow-div')==='')
}),
map(e=>{
const obj={element:document.activeElement,action:null}
switch ((e as KeyboardEvent).keyCode) {
case 38:
obj.action='UP'
break;
case 37:
obj.action='LEFT'
break;
case 40:
obj.action='DOWN'
break;
case 39:
obj.action='RIGTH'
break;
}
return obj
})).subscribe(res=>{
this.sendMessage(res)
})
}
destroy()
{
this.subscription.unsubscribe()
this.subscription=null;
}
}
And a directive like
@Directive({
selector: '[arrow-div]',
})
export class ArrowDivDirective {
element:HTMLElement
constructor(private keyboardService: KeyBoardService,
private el: ElementRef, private render: Renderer2) {
this.render.setAttribute(this.el.nativeElement, "tabindex", "0")
this.element=this.el.nativeElement;
}
}
See that, in this case we not use @HotListener, and that the "element" property is the HTMLElement (not the nativeElement)
We can use in our component like
@ViewChildren(ArrowDivDirective) inputs: QueryList<ArrowDivDirective>;
constructor(private keyboardService: KeyBoardService) {}
ngOnInit() {
this.keyboardService.init();
this.keyboardService.keyBoard.subscribe((res) => {
this.move(res);
});
}
move(object: any) {
const inputToArray = this.inputs.toArray();
let index = inputToArray.findIndex((x) => x.element == object.element);
if (index < 0) index = 0;
else {
switch (object.action) {
case 'UP':
index -= this.columns;
break;
case 'DOWN':
index += this.columns;
break;
case 'LEFT':
index--;
break;
case 'RIGTH':
index++;
break;
case 'RIGTH':
index++;
break;
}
}
if (index >= 0 && index < this.inputs.length) {
inputToArray[index].element.focus();
}
}
ngOnDestroy() {
this.keyboardService.destroy();
}
See that it's necesary "initialize" our service (to began the subscription to fromEvent(document,'keydown'),and, in ngOnDestroy, remove the subscription
The new stackblitz
Answered By - Eliseo
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.