Есть еще один подход с использованием директивы. Идея состоит в том, что мы получаем весь div с нашей директивой в нашем app.component с помощью ViewChildren, затем наш div с директивой отправляет событие и вызывает функцию нашего app.component. Таким образом, app.component становится похожим на
<div arrow-div (event)="handler($event)>my div</div>
<div arrow-div (event)="handler($event)>my div</div>
...
Но мы можем использовать «сервис», чтобы сделать вещи более «прозрачными».
Представьте себе такую услугу, как
@Injectable({
providedIn: 'root',
})
export class KeyBoardService {
keyBoard:Subject<any>=new Subject<any>();
sendMessage(message:any)
{
this.keyBoard.next(message)
}
}
Наша директива может вызывать службу sendMessage при нажатии стрелки на клавиатуре, и в нашем app.component подписаться на эту службу. а затем наш app.component похож на
<div arrow-div >my div</div>
<div arrow-div >my div</div>
<br/>
<div arrow-div >my div</div>
<div arrow-div >my div</div>
Мы избегаем этого "уродливого" (event) = "handler ($ event)" в наших div !!
Ну, директива проста, используя @Hostlistener для прослушивания ключа и renderer2 для добавления атрибута tabindex (чтобы сделать div фокусируемым, нам нужно добавить tabIndex). Так
@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;
}
}
}
И наш app.component.ts
export class AppComponent implements OnInit {
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();
}
}
Обратите внимание, что я использовал переменную «столбец», если мы создаем «сетку» с столбцами и строками и используем клавиши вверх и вниз для перемещения между строками. Отправляя «элемент», избегайте того, чтобы мы сохранили «сфокусированный на div»
Вы можете увидеть пример в stackblitz.
16.06.2019