在外部单击时隐藏组件

ave*_*ave 8 elm

在应该隐藏此组件的单个组件外部处理单击的正确方法是什么?

这种组件的示例可以是下拉菜单,日期选择器等.当我们点击外面时,我们通常希望它们隐藏起来.但要做到这一点,似乎我们必须执行一些"不纯"的黑客攻击,我不确定如何避免FRP风格.

我搜索了相关的React示例并发现了这一点,但它们似乎都依赖于将回调附加到全局对象,然后修改内部组件的状态.

小智 3

以下示例执行与您所描述的类似的操作。

modal会显示一个地址(将“关闭”事件发送到)、当前窗口尺寸和一个 elm-htmlHtml组件(这是要关注的内容,如日期选择器或表单)。

我们将一个点击处理程序附加到周围的元素;给它一个适当的 ID 后,我们可以计算出收到的点击是否适用于它或子项,并适当地转发它们。唯一真正聪明的一点是部署customDecoder过滤掉子元素上的点击。

在其他地方,在收到“关闭”事件后,我们的模型状态会发生变化,这样我们就不再需要调用modal.

这是一个相当大的代码示例,使用了相当多的 elm 包,所以请询问是否需要进一步解释

import Styles exposing (..)

import Html exposing (Attribute, Html, button, div, text)
import Html.Attributes as Attr exposing (style)
import Html.Events exposing (on, onWithOptions, Options)
import Json.Decode as J exposing (Decoder, (:=))
import Result
import Signal exposing (Message)


modal : (Signal.Address ()) -> (Int, Int) -> Html -> Html
modal addr size content = 
    let modalId = "modal"
        cancel = targetWithId (\_ -> Signal.message addr ()) "click" modalId
        flexCss = [ ("display", "flex")
                  , ("align-items", "center")
                  , ("justify-content", "center")
                  , ("text-align", "center")
                  ]
    in div (
            cancel :: (Attr.id modalId) :: [style (flexCss ++ absolute ++ dimensions size)]
           ) [content]

targetId : Decoder String
targetId = ("target" := ("id" := J.string))        

isTargetId : String -> Decoder Bool
isTargetId id = J.customDecoder targetId (\eyed -> if eyed == id then     Result.Ok True else Result.Err "nope!") 

targetWithId : (Bool -> Message) -> String -> String -> Attribute
targetWithId msg event id = onWithOptions event stopEverything (isTargetId id) msg

stopEverything = (Options True True)
Run Code Online (Sandbox Code Playgroud)