relative Haskell and reflex noob here. Decided to get my feet wet with a real-world application.
I am having a problem with triggering an update to a Dynamic containing my record once the user enters text in a textInput.
The code compiles in GHCJS, but once I open up the web page it shows up blank.
If I remove the line marked as problematic (which creates the update event) it works fine (i.e. setting the record from eClient and from the clear button works).
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
Edit: I thought I might be introducing an infinite loop somewhere, so tried a couple of things:
- don't hook up
setEventof the input field and thetextInput_inputevent to the sameDynamic. This did not help - set
setValuetoeClientinstead ofeUpdatedClient- this is theEvent Clientthat we are receiving from outside (e.g. when a row in a table is clicked). Did not help. - trigger the
Dynamicupdate fromtextInput_keypressinstead oftextInput_inputagain to avoid a potential loop (although I think this is not the case here. Did not help.
An infinite loop may well be the problem, though.
Edit: Added another dynText which shows that the event eClientModified fires a perfectly good Client. So it is really in updating the editClient Dynamic that it fails.
Found the cause of my problem in the docs of
tagDyn, ultimately: "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, generallytag (current d) eis preferred."Somehow I expected this to magically work...
So, using the
Behaviorfor the update event instead of theDynamic(andattachWithinstead ofattachPromptlyDynWith) works fine.Here is the working code: