1

Using Fable in an Elmish app, I'd like to listen to the keyboard directly and get each keystroke as a message.

The Elmish documentation has a page on Subscriptions, which shows how to convert JavaScript events to messages. In my case, the events are "keydown" events, and I found code in JavaScript to capture "keydown" events.

However, I'm having trouble putting the F# code together. My issue is that I don't know how to access the keyCode from the event raised when a key is pressed. Here's the code I have so far:

let keyDown initial =
    let sub dispatch =
        document.addEventListener("keydown", fun e ->
            dispatch (KeyDown e.keyCode))  // keyCode is not accessible here
    Cmd.ofSub sub
redcurry
  • 2,381
  • 2
  • 24
  • 38

2 Answers2

2

I think you want to use onKeyDown instead of adding an event listener. If you generate your HTML elements using Feliz, you can dispatch on keydown like this:

Html.input [
    prop.onKeyDown (fun evt ->
        dispatch (KeyDown evt.key))
]

Alternatively, you can probably use dynamic typing to access the property by name (bypassing the type checker):

open Fable.Core.JsInterop

document.addEventListener("keydown", fun e ->
    dispatch (KeyDown e?keyCode))   // use ? operator

I haven't tried that, though, and wouldn't recommend it.

Brian Berns
  • 15,499
  • 2
  • 30
  • 40
  • I'm using Feliz, but the `prop.onKeyDown` doesn't seem to work on `Html.div`. It does work on `Html.input`, but I don't want to show a text box. – redcurry Sep 16 '22 at 14:36
  • I was able to get it to work using the second method, where I put your code above as the first thing in my `render` function. – redcurry Sep 16 '22 at 14:48
  • Actually, putting it in `render` caused the app to behave strangely, so finally I used the "Subscription" method and it works well. Another change I made was to use the `key` property (which gives me the key as a `string`) rather than `keyCode` (which gives me the ASCII number). – redcurry Sep 16 '22 at 17:47
  • 1
    According to [this SO answer](https://stackoverflow.com/a/51267699/344223), you need to give the `div` a `tabindex` value in order for `onKeyDown` to work. – Brian Berns Sep 16 '22 at 19:22
1

In this case you know that the event produced by a keydown is actually a Browser.Types.KeyboardEvent and not only Browser.Types.Event So you can safely downcast

let keydown (event: Event) =
    let keyboardEvent = event :?> KeyboardEvent
    printfn "%A" keyboardEvent.key

document.addEventListener("keydown", keydown)
Peheje
  • 12,542
  • 1
  • 21
  • 30