2015-10-17 4 views
8

Каким будет правильный способ обработки щелчка за пределами одного компонента, который должен скрыть этот компонент?Скрыть компонент при нажатии за пределами

Примером такого компонента может быть выпадающее меню, датпикер и т.п. Обычно мы ожидаем, что они скроются, когда мы выйдем наружу. Но для этого нам кажется, что нам нужно выполнить некоторые «нечистые» хаки, которые я не уверен, как избежать в стиле FRP.

Я искал соответствующие примеры реактивов для идей и нашел this, но все они полагаются на привязку обратных вызовов к глобальным объектам, которые затем изменяют состояние внутренних компонентов.

+1

Не можете ли вы попробовать что-то похожее на найденный материал React? Глобальный обработчик кликов отправляет «Действие», которое обрабатывается функцией «update» родителя компонента (или самого компонента, если вы считаете его частью своей задачи, чтобы знать, когда нужно скрывать), и оценивает, нуждается ли что-то быть скрытым, а затем отразить это в вашей «Модели». – Apanatshka

+0

Благодарим за предложение, я постараюсь сделать что-то подобное, и если это сработает, я сделаю все, что захочу (мне просто нужно сначала узнать о модулях Native js). Моя основная забота об этом пути (предполагая, что вы имеете в виду прикрепление обработчиков к событиям mount/unmount, например, к решениям React), заключалась в том, что это могло бы превзойти цель FRP, если мы будем нарушать наложенные ограничения в любом случае.Но я понял, что это может быть хорошо, если он используется только в редких случаях. – ave

+0

Я не знаю правильного решения в Elm, поэтому я предлагаю вам реплицировать решение React, чтобы исправить вашу проблему сейчас. Но откройте дискуссию по списку рассылки об этой проблеме. Должно быть правильное решение, которое не требует таких хаков;) – Apanatshka

ответ

2

Следующий пример, который делает что-то похожее на то, что вы описываете.

modal представлена ​​с адресом (послать «Отклонить» событие), текущие размеры окна, а компонент ильма-HTML Html (который является вещь, чтобы быть сфокусированы, как DatePicker или форму).

Мы прикрепляем обработчик кликов к окружающему элементу; предоставив ему соответствующий идентификатор, мы сможем разработать, если полученные клики применимы к нему или к ребенку, и направить их соответствующим образом. Единственный действительно умный бит - это развертывание customDecoder для фильтрации кликов на дочернем элементе.

В другом месте, при приеме события «увольнение», наше состояние модели изменяется таким образом, что нам больше не нужно звонить modal.

Это довольно большой пример кода, который использует ярмарки несколько вяза пакетов, поэтому, пожалуйста, спросите, если что требует дополнительного объяснения

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) 
+0

Вау, спасибо, очень полезно ! Также немного похоже на то, что я пробовал до сих пор. Часть расшифровки 'target' интересна. Вероятно, это выходит за рамки вопроса, но просто хотел указать на то, что мне кажется, что неизбежно использовать собственные вызовы JS, чтобы иметь возможность определить положение элемента clicked относительно документа и сгенерированные модальные размеры (по крайней мере, это то, что я сделал в моем случае, минуя проверки безопасности типов и вызывающие собственные функции js, которые используют внутреннее свойство target.getBoundingClientRect(), чтобы модал можно было разместить рядом с целью). – ave

+1

Возможно, вы сможете заменить собственные вызовы с битами http://package.elm-lang.org/packages/TheSeamau5/elm-html-decoder/1.0.1/Html-Decoder (примечание: не мое) – grumpyjames

1

Существующего ответ не работает в вязе v0.18 (Signal был удален в 0.17), поэтому я хотел его обновить. Идея состоит в том, чтобы добавить прозрачный фон верхнего уровня за выпадающим меню. У этого есть бонусный эффект того, что вы можете затемнить все, что находится за меню, если хотите.

Эта примерная модель имеет список слов, и любое слово может иметь открытое раскрывающееся меню (и некоторую связанную информацию), поэтому я сопоставляю их, чтобы увидеть, открыт ли какой-либо из них, и в этом случае я отображаю фоновый div перед всем остальными:

Там есть фон в главной функции вида:

view : Model -> Html Msg 
view model = 
    div [] <| 
     [ viewWords model 
     ] ++ backdropForDropdowns model 

backdropForDropdowns : Model -> List (Html Msg) 
backdropForDropdowns model = 
    let 
     dropdownIsOpen model_ = 
      List.any (isJust << .menuMaybe) model.words 
     isJust m = 
      case m of 
       Just _ -> True 
       Nothing -> False 
    in 
     if dropdownIsOpen model then 
      [div [class "backdrop", onClick CloseDropdowns] []] 
     else 
      [] 

CloseDropdowns обрабатывается в функции обновления верхнего уровня в приложении:

update : Msg -> Model -> (Model, Cmd Msg) 
update msg model = 
    case msg of 
     CloseDropdowns -> 
      let 
       newWords = List.map (\word -> { word | menuMaybe = Nothing }) model.words 
      in 
       ({model | words = newWords}, Cmd.none) 

И стилизованные вещи, использующие scss:

.popup { 
    z-index: 100; 
    position: absolute; 
    box-shadow: 0px 2px 3px 2px rgba(0, 0, 0, .2); 
} 

.backdrop { 
    z-index: 50; 
    position: absolute; 
    background-color: rgba(0, 0, 0, .4); 
    top: 0; 
    right: 0; 
    bottom: 0; 
    left: 0; 
}