Как обращаться с нажатием клавиши ввода в поле ввода?

Я создал простое приложение для учебных целей и хочу иметь возможность отправлять действие, когда пользователь нажимает клавишу Enter в поле ввода

view : Model -> Html Action
  view model = 
    let 
      items = List.map (\ item -> li [] [ text item ]) model.items
    in
      div [] [
       input [ onInput Change, value model.content ] [],
       button [ onClick Add ] [ text "Submit" ],
       ul [] items
      ]

Вот код представления. Надеюсь, этого будет достаточно, чтобы объяснить мои намерения для вас. Я хотел бы иметь возможность отправлять некоторые действия, когда пользователь нажимает клавишу Enter, пока он вводит какой-либо текст в поле ввода.

Ответ 1

Вы можете вручную привязать событие keydown к общему обработчику on. Elm в настоящее время не поддерживает обработчиков onKeyDown из коробки, но они планируются в будущем.

Похоже, что спецификация отходит от event.keyCode и к event.key. Когда это поддерживается в более браузерах, мы можем добавить сюда помощников для onKeyUp, onKeyDown, onKeyPress и т.д. (Источник)

До этого вы можете просто написать свой собственный обработчик и использовать keycode 13 (enter) для выполнения своих действий. Скопируйте следующий код в elm-lang.org/try или откройте https://runelm.io/c/pld, чтобы узнать, как это работает. Просто введите текст в поле ввода и нажмите enter, чтобы увидеть текущее состояние, отраженное в div под полем ввода.

import Html exposing (text, div, input, Attribute)
import Html exposing (beginnerProgram)
import Html.Events exposing (on, keyCode, onInput)
import Json.Decode as Json


main =
  beginnerProgram 
  { model = 
    { savedText = ""
    , currentText = ""
    }
  , view = view
  , update = update
  }


view model =
  div [] 
  [ input [onKeyDown KeyDown, onInput Input] []
  , div [] [ text ("Input: " ++ model.savedText) ]
  ]

onKeyDown : (Int -> msg) -> Attribute msg
onKeyDown tagger =
  on "keydown" (Json.map tagger keyCode)

type Msg 
  = NoOp
  | KeyDown Int
  | Input String


update msg model =
  case msg of

    NoOp ->
      model

    KeyDown key ->
      if key == 13 then
        { model | savedText = model.currentText }
      else
        model

    Input text ->
      { model | currentText = text }

Ответ 2

Есть хорошее, простое решение для обработки onEnter в Elm-версии TodoMVC:

import Html exposing (..)
import Html.Events exposing (keyCode)
import Json.Decode as Json

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

Ответ 3

Вышеуказанные ответы были очень хорошими - но сохранение каждой буквы в Model при каждом нажатии клавиши не всегда является хорошей идеей.

Например, в моем случае у меня есть fileSystem -образный strucutre - и я хочу редактировать любое имя - независимо от того, как он вложен - на doubbleclick. У меня не может быть рельеф дыры fileSystem , при каждом нажатии клавиши. Это лагги.

Я обнаружил, что лучше получить входное значение - только когда пользователь нажимает Enter..

type Msg =
    | EditingStarted
    | EditingFinished String
    | CancelEdit

input [ whenEnterPressed_ReceiveInputValue EditingFinished, whenEscPressed_CancelOperation CancelEdit, onBlur CancelEdit ] []

update msg model =
    case msg of
        EditingFinished inputValue ->
            { model | name = inputValue }
        CancelEdit -> ...


whenEnterPressed_ReceiveInputValue : (String -> msg) -> H.Attribute msg
whenEnterPressed_ReceiveInputValue tagger =
  let
    isEnter code =
        if code == 13 then
            JD.succeed "Enter pressed"
        else
            JD.fail "is not enter - is this error shown anywhere?!"

    decode_Enter =
        JD.andThen isEnter E.keyCode
  in
    E.on "keydown" (JD.map2 (\key value -> tagger value) decode_Enter E.targetValue)


whenEscPressed_CancelOperation : msg -> H.Attribute msg
whenEscPressed_CancelOperation tagger =
  let
    isESC code =
        if code == 27 then
            JD.succeed "ESC pressed"
        else
            JD.fail "it not ESC"

    decodeESC =
        JD.andThen isESC E.keyCode
  in
    E.on "keydown" (JD.map (\key -> tagger) decodeESC)

Примечание. Если вы выполняете отладочную работу во времени - вы будете не видеть каждую букву, появляющуюся по мере ее ввода. Но весь текст сразу - потому что был только один msg. В зависимости от того, что вы делаете - это может быть проблемой. Если нет, наслаждайтесь:)

Ответ 4

Вы можете использовать что-то подобное в своем элементе input, это запустит данное сообщение, если нажать клавишу ввода:

onEnterPressed : msg -> Attribute msg
onEnterPressed msg =
  let
    isEnter code =
      if code == 13 then Ok () else Err ""
    decodeEnterKeyCode = Json.customDecoder keyCode isEnter
  in on "keydown" <| Json.map (\_ -> msg) decodeEnterKeyCode

Ответ 5

Мне понравился ответ Алона, и я немного повторил его, чтобы создать атрибут, который отвечает <enter> и <esc>

onEscEnter : String -> (String -> msg) -> Attribute msg
onEscEnter originalValue tagger =
    let
        handleKey : Int -> Jdec.Decoder Int
        handleKey code =
            if L.member code [ 13, 27 ] then
                -- Enter (13) or ESC (27)
                Jdec.succeed code
            else
                Jdec.fail "something to ignore"

        combiner : Int -> String -> msg
        combiner keyCode tgtVal =
            if keyCode == 13 then
                tagger tgtVal
            else if keyCode == 27 then
                tagger originalValue
            else
                Debug.crash "onEscEnter"

        keyCodeDecoder : Jdec.Decoder Int
        keyCodeDecoder =
            Jdec.andThen handleKey keyCode
    in
        on "keydown" (Jdec.map2 combiner keyCodeDecoder targetValue)

Ответ 6

Если вы хотите использовать пакет сообщества Html.Events.Extra http://package.elm-lang.org/packages/elm-community/html-extra/latest/Html-Events-Extra#onEnter, это очень просто.,

(Предполагая, что вы хотите отправить сообщение Add когда нажата клавиша ввода.)

import Html.Events.Extra exposing (onEnter)

view : Model -> Html Action
  view model = 
    let 
      items = List.map (\ item -> li [] [ text item ]) model.items
    in
      div [] [
       input [ onInput Change, onEnter Add, value model.content ] [],
       button [ onClick Add ] [ text "Submit" ],
       ul [] items
      ]