Обновление записи из текстового ввода
Относительный Хаскелл и рефлекс нуб здесь. Решили намочить ноги с помощью реального приложения.
У меня проблема с запуском обновления в Dynamic, содержащее мою запись, когда пользователь вводит текст в 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
Edit: я думал, что я мог бы ввести бесконечный цикл где-то, поэтому попробовал пару вещей:
- не подключай
setEvent
поля ввода иtextInput_input
событие к тому жеDynamic
, Это не помогло - задавать
setValue
вeClient
вместоeUpdatedClient
- этоEvent Client
что мы получаем извне (например, при щелчке строки в таблице). Не помогло. - вызвать
Dynamic
обновление отtextInput_keypress
вместоtextInput_input
снова, чтобы избежать потенциальной петли (хотя я думаю, что это не так здесь. Не помогло.
Однако бесконечная петля вполне может быть проблемой.
Изменить: добавлен еще один dynText
который показывает, что событие eClientModified
стреляет отлично Client
, Так что это действительно в обновлении editClient
Динамично, что это не удается.
1 ответ
Нашел причину моей проблемы в документах tagDyn
в конечном счете: "Кроме того, это означает, что выходное событие не может быть использовано для непосредственного изменения входной динамики, поскольку это будет означать, что его значение зависит от самого себя. При создании циклических потоков данных обычно tag (current d) e
является предпочтительным ".
Каким-то образом я ожидал, что это волшебным образом сработает...
Итак, используя 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