这个小程序的目的是显示三个按钮,第三个按钮的标签最初为" 0"然后成为最后点击按钮的索引。现在,按钮的数量和其他按钮的标签是不变的。
当我用ghcjs编译这个自包含文件并在浏览器中加载Main.jsexe / index.html时,我可以看到两个traceDyns在一个循环中触发,两者总是具有值0.据我所知,在单击按钮之前不会发生任何事情,因为_el_clicked会为系统的其余部分提供信息。
另请注意,我使用mapDyn (fst . head . Map.toList)
来提取所选按钮的索引 - 我不确定这是否正确,但无论哪种方式我都不知道什么导致无限循环。
{-# LANGUAGE RecursiveDo #-}
module Main where
import Reflex
import Reflex.Dom
import qualified Data.Map as Map
dynButton
:: MonadWidget t m
=> Dynamic t String
-> m (Event t ())
dynButton s = do
(e, _) <- el' "button" $ dynText s
return $ _el_clicked e
-- widget that takes dynamic list of strings
-- and displays a button for each, returning
-- an event of chosen button's index
listChoiceWidget
:: MonadWidget t m
=> Dynamic t [String]
-> m (Event t Int)
listChoiceWidget choices = el "div" $ do
asMap <- mapDyn (Map.fromList . zip [(0::Int)..]) choices
evs <- listWithKey asMap (\_ s -> dynButton s)
k <- mapDyn (fst . head . Map.toList) evs
return $ updated (traceDyn "k" k)
options :: MonadWidget t m => Dynamic t Int -> m (Dynamic t [String])
options foo = do
mapDyn (\x -> ["a", "b", show x]) foo
main :: IO ()
main = mainWidget $ el "div" $ do
rec n <- listChoiceWidget o
o <- options foo
foo <- holdDyn 0 n
display (traceDyn "foo" foo)
答案 0 :(得分:5)
看起来你的listChoiceWidget代码丢弃了dynButton构建的点击事件。
listWithKey
返回m (Dynamic t (Map k a))
。在您的情况下,键的类型为Int
,值为Event t ()
(由dynButton生成)。
在这一行:
k <- mapDyn (fst . head . Map.toList) evs
您正在将Dynamic t (Map Int (Event t ()))
转变为Dynamic t Int
但是,至关重要的是,当点击事件触发时您不会这样做。此行映射到evs
并生成一个Dynamic,它始终包含Int to Events Map中的第一个键,无论事件是否已触发。它始终是包含Int 0的动态。
您看到循环的原因是:
main
将foo
的初始值0引入options
listChoiceWidget
收到新选项并更新列表foo
收到来自listChoiceWidget
您需要某种方法来确定最后一次按钮点击事件,而不是从地图中检索第一个键。您的地图已包含显示的每个按钮的点击事件。现在这些事件的类型为Event t ()
,但你真正需要的是Event t Int
,所以当事件触发时你可以告诉它来自哪个按钮。
evs' <- mapDyn (Map.mapWithKey (\k e -> fmap (const k) e)) evs
evs'
的类型为Dynamic t (Map Int (Event t Int))
。接下来,我们需要一些方法来组合我们的事件,以便我们使用最近点击的按钮键激活一个事件。
dynEv <- mapDyn (leftmost . Map.elems . Map.mapWithKey (\k e -> fmap (const k) e)) evs
dynEv
现在的类型为Dynamic t (Event t Int)
。地图的关键已经融入事件中,因此我们不再需要它们了。 Map.elems
将我们的事件地图转换为事件列表,leftmost
允许您将事件列表合并为一个事件。
来自leftmost
的{{3}}:&#34;如果列表中至少有一个事件发生,则创建一个新事件。如果多个同时出现,则使用给定的函数从左侧折叠。&#34;
最后,我们需要将您的Dynamic t (Event t Int)
转换为Event t Int
。我们将使用switch
,Behavior t (Event t a)
并返回Event t a
。因此,以下行将生成Event t Int
。
switch (current dynEv)
current
提取Behavior
的{{1}},Dynamic
创建&#34;当前选择的输入事件发生时将发生的事件。&# 34;
这是修订后的switch
代码。我已经包含了内联类型注释,因此您需要启用listChoiceWidget
语言扩展来编译此代码(或者您可以删除注释)。
ScopedTypeVariables