没有框架的 Elm 中的模态

Eve*_*per 1 elm elm-ui

我是 ELM 的新手,我想在不使用任何库(例如 Bootstrap 或 ELM-UI)的情况下创建模态。我在网上找到了这个简单的例子,它也使用了 JSON 解码。是否有可能在没有任何框架/库和 JSON 解码的情况下让模态工作?如何修改代码以简单地获得工作模式?

module Main exposing (main)

import Browser
import Html exposing (Html, Attribute, button, div, span, text)
import Html.Events exposing (onClick, on)
import Html.Attributes exposing (class, style)
import Json.Decode as Decode


type alias Model =
    { isVisible : Bool, count : Int }


initialModel : Model
initialModel =
    { isVisible = False, count = 0 }


type Msg
    = Show
    | Hide
    | Increment
    | Decrement


update : Msg -> Model -> Model
update msg model =
    case msg of
        Show ->
            { model | isVisible = True }

        Hide ->
            { model | isVisible = False }
            
        Increment ->
            { model | count = model.count + 1 }
            
        Decrement ->
            { model | count = model.count - 1 }


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Show ] [ text "Show!" ]
        , if model.isVisible then
            div
                ([ class dialogContainerClass
                 , on "click" (containerClickDecoder Hide)
                 ]
                    ++ dialogContainerStyle
                )
                [ div dialogContentStyle
                    [ span [] [ text "Click anywhere outside this dialog to close it!" ]
                    , span [] [ text "Clicking on anything inside of this dialog works as normal." ]
                    , div []
                        [ button [ onClick Decrement ] [ text "-" ]
                        , text (String.fromInt model.count)
                        , button [ onClick Increment ] [ text "+" ]
                        ]
                    ]
                ]
          else
            div [] []
        ]


dialogContainerClass : String
dialogContainerClass = "dialog-container-class"


containerClickDecoder : msg -> Decode.Decoder msg
containerClickDecoder closeMsg =
    Decode.at [ "target", "className" ] Decode.string
        |> Decode.andThen
            (\c ->
                if String.contains dialogContainerClass c then
                    Decode.succeed closeMsg

                else
                    Decode.fail "ignoring"
            )



dialogContainerStyle : List (Attribute msg)
dialogContainerStyle =
    [ style "position" "absolute"
    , style "top" "0"
    , style "bottom" "0"
    , style "right" "0"
    , style "left" "0"
    , style "display" "flex"
    , style "align-items" "center"
    , style "justify-content" "center"
    , style "background-color" "rgba(33, 43, 54, 0.4)"
    ]
    
    
dialogContentStyle : List (Attribute msg)
dialogContentStyle =
    [ style "border-style" "solid"
    , style "border-radius" "3px"
    , style "border-color" "white"
    , style "background-color" "white"
    , style "height" "120px"
    , style "width" "440px"
    , style "display" "flex"
    , style "flex-direction" "column"
    , style "align-items" "center"
    , style "justify-content" "center"
    ]


main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel
        , view = view
        , update = update
        }
Run Code Online (Sandbox Code Playgroud)

Igi*_*gid 5

如果我正确理解了您的问题,那么您要解决的问题是单击模态外部以将其关闭。解码事件对象以获取有关 DOM 的信息是 Elm 中的一个小技巧——我认为您尝试避免它是正确的,除非必要。实现相同目的的一种方法是添加一个带有停止传播的单击事件处理程序到您的模态内容——这会阻止单击事件在来自模态内部时在容器上触发。

我已将您的示例代码放在 Ellie 中并进行了一些小改动:https : //ellie-app.com/b9gDPHgtz2ca1

此解决方案使用Html.Events.stopPropagationOn,它类似于on但调用event.stopPropagation(). 这个函数确实需要你提供一个解码器,所以恐怕你无法摆脱 import Json.Decode,但我们正在使用最简单的解码器Decode.succeed——而且只是为了满足函数的参数。

我在 中添加了一个NoOp变体Msg,因为单击模态时没有任何事情可做;简单地附加这个事件处理程序可以阻止Hide事件在我们不想要的时候触发。

代码

module Main exposing (main)

import Browser
import Html exposing (Attribute, Html, button, div, span, text)
import Html.Attributes exposing (class, style)
import Html.Events exposing (on, onClick)
import Json.Decode as Decode


type alias Model =
    { isVisible : Bool, count : Int }


initialModel : Model
initialModel =
    { isVisible = False, count = 0 }


type Msg
    = Show
    | Hide
    | Increment
    | Decrement
    | NoOp


update : Msg -> Model -> Model
update msg model =
    case msg of
        Show ->
            { model | isVisible = True }

        Hide ->
            { model | isVisible = False }

        Increment ->
            { model | count = model.count + 1 }

        Decrement ->
            { model | count = model.count - 1 }

        NoOp ->
            model


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Show ] [ text "Show!" ]
        , if model.isVisible then
            div
                (onClick Hide
                    :: dialogContainerStyle
                )
                [ div
                    (onClickStopPropagation NoOp
                        :: dialogContentStyle
                    )
                    [ span [] [ text "Click anywhere outside this dialog to close it!" ]
                    , span [] [ text "Clicking on anything inside of this dialog works as normal." ]
                    , div []
                        [ button [ onClick Decrement ] [ text "-" ]
                        , text (String.fromInt model.count)
                        , button [ onClick Increment ] [ text "+" ]
                        ]
                    ]
                ]

          else
            div [] []
        ]


onClickStopPropagation : msg -> Html.Attribute msg
onClickStopPropagation msg =
    Html.Events.stopPropagationOn "click" <| Decode.succeed ( msg, True )


dialogContainerStyle : List (Attribute msg)
dialogContainerStyle =
    [ style "position" "absolute"
    , style "top" "0"
    , style "bottom" "0"
    , style "right" "0"
    , style "left" "0"
    , style "display" "flex"
    , style "align-items" "center"
    , style "justify-content" "center"
    , style "background-color" "rgba(33, 43, 54, 0.4)"
    ]


dialogContentStyle : List (Attribute msg)
dialogContentStyle =
    [ style "border-style" "solid"
    , style "border-radius" "3px"
    , style "border-color" "white"
    , style "background-color" "white"
    , style "height" "120px"
    , style "width" "440px"
    , style "display" "flex"
    , style "flex-direction" "column"
    , style "align-items" "center"
    , style "justify-content" "center"
    ]


main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel
        , view = view
        , update = update
        }
Run Code Online (Sandbox Code Playgroud)