相对Haskell和反射noob在这里。决定用现实世界的应用程序弄湿我的脚。
一旦用户在textInput
中输入文字,我就会遇到触发包含我的记录的动态更新的问题。
代码在GHCJS中编译,但是一旦我打开网页,它就显示为空白。
如果我删除标记为有问题的行(创建更新事件),它可以正常工作(即从eClient
设置记录并从清除按钮设置)。
data Client = Client
{ _clientName :: Text
, _contacts :: [Text] -- TODO make a new type for this
, _balance :: Int -- this is calculated
, _notes :: [Text] -- free text notes, might come in handy
} deriving (Show, Eq)
updateFieldFromTextInput :: Reflex t =>
(Client -> T.Text -> Client) ->
Dynamic t Client ->
Event t T.Text ->
Event t Client
updateFieldFromTextInput setter dynClient evInput = attachPromptlyDynWith setter dynClient evInput
-- the input event is the one to set a client on the widget
-- the output event is when a client is saved
clientEditWidget :: MonadWidget t m => Event t Client -> m (Event t Client)
clientEditWidget eClient = mdo
(editClient, eSaveButton) <- elClass "div" "client-edit" $ mdo
-- fires an Event t Client when the input field is changed
let eNameInput = (nameInput ^. textInput_input)
nameSetter = flip (clientName .~)
eNameUpdate = updateFieldFromTextInput nameSetter editClient eNameInput
eClear = mkClient "" <$ eClearButton
eClientReplaced = leftmost [eClient, eClear]
eClientModified = leftmost [eNameUpdate]
-- the currently edited client
-- using eClientModified causes a blank screen
-- editClient <- holdDyn (mkClient "") eClientModified
editClient <- holdDyn (mkClient "") eClientReplaced
-- lay out the widgets
text "edit client"
nameInput <- textInput $
def & setValue .~
((view clientName) <$> eClientReplaced)
contactsInput <- textArea $
def & setValue .~
((T.concat . view contacts) <$> eClientReplaced)
eSaveButton <- button "Save"
eClearButton <- button "Clear"
dynText =<< holdDyn "updated client will appear here" (T.pack . show <$> eClientModified)
return (editClient, eSaveButton)
return $ tagPromptlyDyn editClient eSaveButton
编辑:我以为我可能会在某个地方引入无限循环,所以尝试了几件事:
setEvent
与textInput_input
事件挂钩到同一Dynamic
。这没有帮助setValue
设置为eClient
而不是eUpdatedClient
- 这是我们从外部接收的Event Client
(例如,当点击表格中的行时)。没有帮助。Dynamic
触发textInput_keypress
更新,而不是textInput_input
,以避免潜在的循环(虽然我认为这不是这种情况。没有帮助。无限循环可能是问题所在。
修改:添加了另一个dynText
,表明事件eClientModified
触发了一个非常好的Client
。所以它确实在更新失败的editClient
动态。
答案 0 :(得分:2)
在tagDyn
的文档中找到问题的原因,最终:&#34;此外,这意味着输出事件可能不会用于直接更改输入Dynamic,因为这意味着它的值取决于自己。创建循环数据流时,通常首选tag (current d) e
。&#34;
不知怎的,我预计这会神奇地工作......
因此,使用Behavior
代替更新事件代替Dynamic
(以及attachWith
代替attachPromptlyDynWith
)可以正常使用。
以下是工作代码:
updateFieldFromTextInput :: Reflex t =>
(Client -> T.Text -> Client) ->
Behavior t Client ->
Event t T.Text ->
Event t Client
updateFieldFromTextInput setter bClient evInput = attachWith setter bClient evInput
-- the input event is the one to set a client on the widget
-- the output event is when a client is saved
clientEditWidget :: MonadWidget t m => Event t Client -> m (Event t Client)
clientEditWidget eClient = mdo
(editClient, eSaveButton) <- elClass "div" "client-edit" $ mdo
-- fires an Event t Client when the input field is changed
let eNameInput = (nameInput ^. textInput_input)
nameSetter = flip (clientName .~)
eNameUpdate = updateFieldFromTextInput nameSetter (current editClient) eNameInput
eClear = mkClient "" <$ eClearButton
eClientReplaced = leftmost [eClient, eClear]
eClientModified = leftmost [eNameUpdate]
-- the currently edited client
editClient <- holdDyn (mkClient "") eClientModified
-- lay out the widgets
text "edit client"
nameInput <- textInput $
def & setValue .~
((view clientName) <$> eClientReplaced)
contactsInput <- textArea $
def & setValue .~
((T.concat . view contacts) <$> eClientReplaced)
eSaveButton <- button "Save"
eClearButton <- button "Clear"
dynText =<< holdDyn "updated client will appear here" (T.pack . show <$> eClientModified)
return (editClient, eSaveButton)
return $ tagPromptlyDyn editClient eSaveButton