Gestione eventi tastiera in Elm

Posted on Feb 12, 2021

Elm è un linguaggio di tipo funzionale con uno dominio di utilizzo ben preciso: è pensato per creare interfacce grafiche per il web. La compilazione di un programma in Elm produce direttamente tutto il codice javascript che il browser deve interpretare.

Curioso di provare questo linguaggio, perché interessato ad imparare le basi della programmazione funzionale, ho deciso di utilizzarlo in un piccolo progetto di studio ts-converter.

Sviluppando questo progetto ho dovuto capire come gestire con Elm gli eventi che arrivano dalla tastiera. L’esempio presenta la gestione dell’evento enter.

L’idea di questo articolo è fornire alcune note per poter gestire questa attività nella propria applicazione.

Due sono gli scenari possibili

Scenario 1: Catturare tutti gli eventi dell’applicazione

Vediamo come catturare qualunque tasto premuto all’interno dell’applicazione Per questo scenario abbiamo bisogno delle subscription

Prima di tutto si deve registrare una subscription

Una subscription è un’operazione che ci permette di ricevere un evento esterno Creandone una si comunica al runtime di Elm che vogliamo compiere un’azione al manifestarsi dell’evento. In particolare il runtime produce un messaggio di update quando viene ascoltato l’evento di interesse.

subscriptions _ =
  onKeyPress keyDecoder

Viene creata una sottoscrizione all’evento onKeyPress, quando questo intervento viene intercettato viene invocata la funzione keyDecoder

keyDecoder : Decode.Decoder Msg
keyDecoder =
    Decode.map toKey (Decode.field "key" Decode.string)

Decode.field "key" Decode.string permette di trasformare elementi JSON in valori validi per Elm. In particolare l’evento catturato viene rappresentato come un oggetto serializzato in JSON e nel cui campo key viene inserito il valore del tasto che è stato premuto Il valore di tale tasto viene passato alla funzione toKey.

toKey : String -> Msg
toKey keyValue =
    case String.uncons keyValue of
        Just ( char, "" ) ->
            CharacterKey char
        _ ->
            ControlKey keyValue

String.uncons considera la Stringa come una array di caratteri e ne divide la testa dal resto dell’array. A seconda se il carattere di testa è un valore alfanumerico o meno produce il relativo messaggio di update

CharacterKey k ->
      ( model
      , Cmd.none
      )
    ControlKey k ->
      ( { model | result = formatter } 
      , Cmd.none
      )

Nella funzione di update verranno gestiti i due casi.

Scenario 2: Catturare un evento legato a un campo di input

Elm non supporta al momento (0.19) nativamente onEnter, ma offre la funzione on che permette di creare un custom event listener

onEnter: Msg -> Attribute Msg
onEnter msg =
  let 
    isEnter code = 
      if code == 13 then
        Decode.succeed msg
      else
        Decode.fail "not ENTER"
  in
on "keydown" (Decode.andThen isEnter keyCode)

Con on "keydown" (Decode.andThen isEnter keyCode) creiamo un listener in ascolto sull’evento keydown e al suo manifestarsi eseguiamo (Decode.andThen isEnter keyCode) dove keyCode è la funzione che ritorna il valore intero del tasto premuto, isEnter controlla se e' stato premuto il tasto che ci interessa enter (valore 13) producendo rispettivamente un messaggio per la funzione di update o un fallimento

La funzione onEnter viene usata nel campo input che ci interessa monitorare.

input [ size 20, value (tsToString model.ts), onInput SetTs, onEnter Convert, autofocus True] []

Maggiori dettagli

https://elmprogramming.com/subscriptions.html :