2016-09-27 230 views
7

我想創建一個高階函數來創建函數來只捕獲特定的密鑰代碼。代碼的啓發來自他的todomvc實現中的Evan的「onEnter」函數,該函數僅捕獲輸入函數。在鍵盤事件中使用elm高階函數

onKeyCode : Int -> Msg -> Attribute Msg 
onKeyCode keycode msg = 
    let 
     captureKey code = 
      if code == keycode then 
       msg 
      else 
       NoOp 
    in 
     on "keydown" (Json.map captureKey keyCode) 

onEnter = onKeyCode 13 
onEsc = onKeyCode 27 

,現在我想將它添加到輸入組件在觀衆:

input 
[ class "edit" 
, id ("todo-" ++ toString item.uid) 
, value item.message 
, onInput (UpdateItem item.uid) 
, onBlur (SwitchEditTodo item.uid False) 
, onEnter (SwitchEditTodo item.uid False) 
, onEsc (UndoEditTodo item.uid) 
] 
[] 

如果我只按照預期的OnEnter代碼將工作,但如果我添加了onEsc的的OnEnter代碼從未執行。我在哪裏做錯了?是在高級函數上下文中存在問題還是在分開的函數中使用多個值進行「on」映射?

回答

8

您正在爲輸入元素添加兩個onkeydown屬性,並且只有其中一個可以獲勝。列表中的第二個覆蓋第一個。如果在幕後使用addEventListener,情況並非如此,但現在我們可以用稍微不同的方法解決它。

你可以寫一個onKeysDown函數接受可能的關鍵代碼的列表,他們應該調用類似這樣的消息:

onKeysDown : List (Int, Msg) -> Attribute Msg 
onKeysDown keys = 
    let 
    captureKey code = 
     List.filterMap (\(k, m) -> if k == code then Just m else Nothing) keys 
     |> List.head 
     |> Maybe.withDefault NoOp 
    in 
    on "keydown" (Json.map captureKey keyCode) 

然後,您可以寫速記功能,用於處理特定鍵這樣的:

enter msg = (13, msg) 
esc msg = (27, msg) 

現在您可以在視圖中使用這樣的:

input 
    [ ... 
    , onKeysDown 
     [ enter <| SwitchEditTodo item.uid False 
     , esc <| UndoEditTodo item.uid 
     ] 
    ] 

這是可行的,因爲它會生成一個單一的​​事件處理程序屬性。當你用一個預定義的元組替換一個更高階的函數時,它仍然給你一樣的可讀代碼。

+2

從我的理解來看,這種覆蓋效應是HTML/JavaScript產生的副作用,因爲即使我已經創建了幾個「on」事件,我畢竟只是在封面上調用JavaScript API,它允許我覆蓋自己。 –

+4

正確,它是同名多個html屬性的限制,不是Elm的限制(雖然有些警告會很好)。如果您嘗試在原始html中執行相同的操作,則會看到相同的問題 –