从文本输入更新记录
Updating a record from a text input
相对Haskell和反射菜鸟在这里。决定用 real-world 应用程序弄湿我的脚。
一旦用户在 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
(例如,当 [=49= 中的一行] 被点击)。没有帮助。
- 再次从
textInput_keypress
而不是textInput_input
触发Dynamic
更新以避免潜在的循环(虽然我认为这里不是这种情况。没有帮助。
不过,无限循环很可能是问题所在。
编辑:添加了另一个 dynText
,它表明事件 eClientModified
触发了一个非常好的 Client
。所以它确实在更新 editClient
动态时失败了。
在 tagDyn
的文档中找到了我的问题的原因,最终:"Additionally, this means that the output Event may not be used to directly change the input Dynamic, because that would mean its value depends on itself. When creating cyclic data flows, generally tag (current d) e
is preferred."
不知何故,我希望这会神奇地起作用...
因此,对更新事件使用 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
相对Haskell和反射菜鸟在这里。决定用 real-world 应用程序弄湿我的脚。
一旦用户在 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
(例如,当 [=49= 中的一行] 被点击)。没有帮助。 - 再次从
textInput_keypress
而不是textInput_input
触发Dynamic
更新以避免潜在的循环(虽然我认为这里不是这种情况。没有帮助。
不过,无限循环很可能是问题所在。
编辑:添加了另一个 dynText
,它表明事件 eClientModified
触发了一个非常好的 Client
。所以它确实在更新 editClient
动态时失败了。
在 tagDyn
的文档中找到了我的问题的原因,最终:"Additionally, this means that the output Event may not be used to directly change the input Dynamic, because that would mean its value depends on itself. When creating cyclic data flows, generally tag (current d) e
is preferred."
不知何故,我希望这会神奇地起作用...
因此,对更新事件使用 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