2017-02-22 12 views
5

Я пытаюсь сделать редактируемый тег контента, который использует , ввести, чтобы обновить модель.Elm: Conditional preventDefault (with contentEditable)

Мой код приведен ниже, и вот версия, с которой вы можете играть по телефону Ellie.

Атрибут «размытие» работает и обновляет модель при нажатии. Но я хочу получить ту же функциональность «обновления», когда нажата кнопка .

view : Model -> Html Msg 
view model = 
    let 
     attrs = 
      [ contenteditable True 
       --, on "blur" (Json.map UpdateTitle targetTextContent) 
      , onInput2 UpdateTitle 
      , onEnter EnterPressed 
      , id "title" 
      , class "title" 
      ] 
    in 
     div [] 
      [ h1 attrs [ text model.existing ] 
      , text "Click above to start editing. Blur to save the value. The aim is to capture an <enter> and interpret that as a blur, i.e. to save the value and blur the field" 
      , p [] [ text <| "(" ++ model.existing ++ ")" ] 
      ] 


targetTextContent : Json.Decoder String 
targetTextContent = 
    Json.at [ "target", "textContent" ] Json.string 


onInput2 : (String -> msg) -> Attribute msg 
onInput2 msgCreator = 
    on "input" (Json.map msgCreator targetTextContent) 


onEnter : (Bool -> msg) -> Attribute msg 
onEnter enterMsg = 
    onWithOptions "keydown" 
     { stopPropagation = False 
     , preventDefault = False 
     } 
     (keyCode 
      |> Json.andThen 
       (\ch -> 
        let 
         _ = 
          Debug.log "on Enter" ch 
        in 
         Json.succeed (enterMsg <| ch == 13) 
       ) 
     ) 

Этот код, кажется, обновляет модель в порядке, но DOM становится испорченным. Например, если я введу ввести после «взрыва» Я вижу это

enter image description here

Я попытался переключиться на Html.Keyed и используя «KeyDown», но это не имеет никакого значения или только что создали различные вопросы.

+0

Хотя у меня нет ответа на ваш вопрос, я считаю, что он имеет какое-то отношение к использованию виртуального DOM. Вы можете исследовать, как Реакт имеет дело с этим. например. http://stackoverflow.com/q/30601516/1238847 – Tosh

ответ

6

Решенный! Ключевым моментом является функция фильтра, которая использует Json.Decode.fail, так что только <enter> подлежит предотвращениюDefault. См. https://github.com/elm-lang/virtual-dom/issues/18#issuecomment-273403774 за идею.

view : Model -> Html Msg 
view model = 
    let 
     attrs = 
      [ contenteditable True 
      , on "blur" (Json.map UpdateTitle targetTextContent) 
      , onEnter EnterPressed 
      , id "title" 
      , class "title" 
      ] 
    in 
     div [] 
      [ h1 attrs [ text model.existing ] 
      , text "Click above to start editing. Blur to save the value. The aim is to capture an <enter> and interpret that as a blur, i.e. to save the value and blur the field" 
      , p [] [ text <| "(" ++ model.existing ++ ")" ] 
      ] 


targetTextContent : Json.Decoder String 
targetTextContent = 
    Json.at [ "target", "textContent" ] Json.string 


onEnter : msg -> Attribute msg 
onEnter msg = 
    let 
     options = 
      { defaultOptions | preventDefault = True } 

     filterKey code = 
      if code == 13 then 
       Json.succeed msg 
      else 
       Json.fail "ignored input" 

     decoder = 
      Html.Events.keyCode 
       |> Json.andThen filterKey 
    in 
     onWithOptions "keydown" options decoder