2015-10-17 27 views
8

Cách nào đúng để xử lý nhấp chuột bên ngoài một thành phần duy nhất được cho là ẩn thành phần này?Ẩn thành phần khi được nhấp vào bên ngoài

Ví dụ về thành phần như vậy có thể là menu thả xuống, trình chỉnh sửa ngày và các loại tương tự. Chúng tôi thường mong đợi họ ẩn khi chúng tôi nhấp vào bên ngoài. Nhưng để làm như vậy, có vẻ như chúng tôi phải thực hiện một số "không tinh khiết" hack mà tôi không chắc chắn làm thế nào để tránh trong phong cách FRP.

Tôi đã tìm kiếm các ví dụ React có liên quan cho các ý tưởng và tìm thấy this nhưng tất cả dường như đều dựa vào việc gắn callbacks vào các đối tượng toàn cục, sau đó sửa đổi trạng thái của thành phần bên trong.

+1

Bạn có thể thử một thứ gì đó tương tự với nội dung React mà bạn tìm thấy không? Trình xử lý nhấp chuột toàn cục gửi 'Hành động' được xử lý trong hàm' update' của cha mẹ của thành phần (hoặc chính thành phần đó, nếu bạn coi đó là một phần của nhiệm vụ cần biết khi nào bị ẩn) và đánh giá xem có cần gì không bị ẩn, sau đó phản ánh điều đó trong 'Mô hình' của bạn. – Apanatshka

+0

Cảm ơn bạn đã đề xuất, tôi sẽ cố gắng làm một việc như vậy và nếu nó hoạt động, tôi sẽ giải quyết hầu hết (tôi chỉ cần tìm hiểu thêm về mô-đun js Native trước). Mối quan tâm chính của tôi khi đi với con đường này (giả sử bạn có nghĩa là gắn các trình xử lý trên các sự kiện mount/unmount như trên các giải pháp React) là điều này có thể đánh bại mục đích của FRP nếu chúng ta sẽ phá vỡ các hạn chế áp đặt.Nhưng tôi nhận ra rằng nó có thể ổn nếu nó chỉ được sử dụng trong những dịp hiếm hoi như vậy. – ave

+0

Tôi không biết một giải pháp thích hợp trong Elm, vì vậy đó là lý do tại sao tôi đề nghị bạn tái tạo giải pháp React để khắc phục sự cố của bạn ngay bây giờ. Nhưng hãy mở một cuộc thảo luận về danh sách gửi thư về vấn đề này. Nên có một giải pháp thích hợp mà không yêu cầu những loại hack này;) – Apanatshka

Trả lời

2

Ví dụ sau thực hiện điều gì đó tương tự như những gì bạn mô tả.

modal được trình bày với một địa chỉ (để gửi sự kiện 'loại bỏ'), kích thước cửa sổ hiện tại và thành phần elm-html Html (là thứ cần được tập trung, như biểu tượng ngày hoặc biểu mẫu).

Chúng tôi đính kèm trình xử lý nhấp chuột vào phần tử xung quanh; đã cho nó một id thích hợp, chúng tôi có thể làm việc ra nếu nhận được nhấp chuột áp dụng cho nó hoặc đứa trẻ, và chuyển tiếp chúng một cách thích hợp. Bit duy nhất thực sự thông minh là triển khai customDecoder để lọc ra các nhấp chuột trên phần tử con.

Ở những nơi khác, khi nhận được sự kiện 'loại bỏ', trạng thái mô hình của chúng tôi thay đổi sao cho chúng tôi không cần phải gọi số modal nữa.

này là khá một mẫu mã lớn mà làm cho việc sử dụng một vài gói elm công bằng, vì vậy hãy hỏi bất cứ điều gì cần giải thích thêm

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

Wow, cảm ơn rất nhiều, điều này rất hữu ích ! Ngoài ra một chút tương tự như những gì tôi đã cố gắng cho đến nay. Phần giải mã của 'target' là thú vị. Nó có lẽ nằm ngoài phạm vi của câu hỏi nhưng chỉ muốn chỉ ra rằng dường như với tôi rằng không thể tránh khỏi việc sử dụng các cuộc gọi JS để có thể tìm ra vị trí phần tử được nhấp tương ứng với tài liệu và các thứ nguyên phương thức được tạo ra (ít nhất đó là những gì tôi đã làm trong trường hợp của tôi, bỏ qua kiểm tra an toàn kiểu và gọi các hàm ban đầu của js sử dụng 'target.getBoundingClientRect()' nội bộ để phương thức có thể được đặt gần đích). – ave

+1

Bạn có thể thay thế các cuộc gọi gốc mà bạn có bằng các bit http://package.elm-lang.org/packages/TheSeamau5/elm-html-decoder/1.0.1/Html-Decoder (lưu ý: không phải của tôi) – grumpyjames

1

Câu trả lời hiện không hoạt động trong elm v0.18 (Signal đã bị xóa trong 0,17), vì vậy tôi muốn cập nhật nó. Ý tưởng là thêm phông nền trong suốt cấp cao nhất phía sau trình đơn thả xuống. Điều này có tác dụng tiền thưởng của việc có thể làm tối tất cả mọi thứ đằng sau menu nếu bạn muốn.

Mẫu ví dụ này có danh sách các từ và bất kỳ từ nào có thể có danh sách thả xuống mở (và một số thông tin liên quan), vì vậy tôi ánh xạ chúng để xem có bất kỳ từ nào đang mở hay không. trước mọi thứ khác:

có một bối cảnh trong hàm giao diện chính:

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 được xử lý trong chức năng cập nhật top-level của ứng dụng:

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) 

Và những thứ được tạo kiểu bằng cách sử dụng 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; 
} 
Các vấn đề liên quan