Как сделать IO в виджете / гамлете, на который ссылается defaultLayout?
Я новичок в Йесод и, похоже, полностью потерялся с виджетами, хендлерами, хамлетами, хамлетами и тем, что у тебя есть! Вот что я пытаюсь сделать:
- Каждая страница на моем сайте должна иметь навигационную панель, что заставляет меня полагать, что правильное место для реализации этого должно быть
defaultLayout
- Теперь эта панель навигации должна отображать некоторую информацию, полученную от действия ввода-вывода (это вызов RPC, который дает эти данные, если быть более точным).
Поэтому я попытался написать следующую функцию в Foundation.hs
(макет кода является основным yesod-sqlite
шаблон лесов):
nav = do
globalStat <- handlerToWidget $ A2.getGlobalStat NWT.ariaRPCUrl
$(whamletFile "templates/navbar.hamlet)
A2.getGlobalStat :: IO GlobalStatResponse
Вот что template/navbar.hamlet
похоже:
<nav .navbar .navbar-default>
<div .container-fluid>
<p .navbar-right .navbar-text>
<span>
#{A2.glDownloadSpeed globalStat}
<i .glyphicon .glyphicon-arrow-down>
<span>
#{A2.glUploadSpeed globalStat}
<i .glyphicon .glyphicon-arrow-up>
<span .label .label-success>
On-the-watch
Вот что default-layout-wrapper.hamlet
похоже:
<!-- SNIP -->
<body>
<div class="container">
<header>
^{nav}
<div id="main" role="main">
^{pageBody pc}
<!-- SNIP -->
Вот что defaultLayout
похоже:
defaultLayout widget = do
master <- getYesod
mmsg <- getMessage
pc <- widgetToPageContent $ do
addStylesheet $ StaticR css_bootstrap_css
$(widgetFile "default-layout")
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
Однако код отказывается компилироваться с одной ошибкой типа за другой. Я перепробовал много комбинаций hametFile
, whamletFile
, handerToWidget
, liftIO
, даже поместив функцию навигации внутри defaultLayout
, но ничего не работает. По моему мнению мой текущий код должен компилироваться, но я, очевидно, не понял, как работают типы Yesod-Core.
Как мне заставить это работать? И что еще более важно, какую концепцию я неправильно понял?
Изменить 1:
Пытался модифицировать nav
функция к следующему:
nav :: Handler Html
nav = do
globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
$(hamletFile "templates/navbar.hamlet")
Но это приводит к следующему несоответствию типов в defaultLayout
на линии с withUrlRenderer
:
Couldn't match type ‘HandlerT App IO Html’
with ‘Text.Hamlet.Render (Route App) -> Html’
Expected type: HtmlUrl (Route App)
Actual type: Handler Html
In the first argument of ‘Text.Hamlet.asHtmlUrl’, namely ‘nav’
In a stmt of a 'do' block: Text.Hamlet.asHtmlUrl nav _render_a2ZY0 (intero)
Изменить 2:
Попробовал изменить тип подписи nav
чтобы:
nav :: Widget
nav = do
globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
$(hamletFile "templates/navbar.hamlet")
Но это приводит к новому несоответствию типов в той же строке:
Couldn't match type ‘WidgetT App IO ()’
with ‘Text.Hamlet.Render (Route App) -> Html’
Expected type: HtmlUrl (Route App)
Actual type: Widget
In the first argument of ‘Text.Hamlet.asHtmlUrl’, namely ‘nav’
In a stmt of a 'do' block: Text.Hamlet.asHtmlUrl nav _render_a350l (intero)
Изменить 3:
Вот соответствующий фрагмент из -ddump-splices
:
\ _render_a28TE
-> do { asHtmlUrl (pageHead pc) _render_a28TE;
id ((Text.Blaze.Internal.preEscapedText . Data.Text.pack) "\n");
asHtmlUrl (pageBody pc) _render_a28TE;
id ((Text.Blaze.Internal.preEscapedText . Data.Text.pack) "\n");
asHtmlUrl testWidget2 _render_a28TE }
Тип (pageHead pc)
а также (pageBody pc)
является HtmlUrl (Route App)
2 ответа
Посмотрите на ответ на этот ТАК вопрос. По сути, вы не можете выполнить IO в шаблоне.
Также обратите внимание, что тип defaultLayout
является GHandler ...
а также GHandler
является экземпляром MonadIO, поэтому вы можете выполнить IO в defaultLayout
используя liftIO
,
Я бы попробовал:
defaultLayout = do
...
globalStat <- liftIO $ handlerToWidget $ A2.getGlobalStat NWT.ariaRPCUrl
uploadSpeed <- liftIO $ A2.glUploadSpeed globalStat
downloadSpeed <- liftIO $ A2.glDownloadSpeed globalStat
...
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
И в templates/default-layout-wrapper.hamlet
:
...
^{nav uploadSpeed downloadSpeed}
...
А также nav
становится что-то вроде:
nav uploadSpeed downloadSpeed = $(whamletFile "templates/navbar.hamlet)
Итак, основные идеи:
- Сделайте все свои IO в
defaultLayout
с помощьюliftIO
- Передать данные, необходимые суб-шаблонам, в качестве аргументов функции
Обновить
Чтобы подражать этому примеру в книге Yesod, вам нужно написать navbar
как это:
navbar :: Widget
navbar = do
globalStat <- liftIO A2.getGlobalStat NWT.ariaRPCUrl
downloadSpeed <- liftIO A2.glDownloadSpeed globalStat
uploadSpeed <- liftIO A.glUploadSpeed
$(whamletFile "templates/navbar.hamlet)
И в navbar.whamlet
Ссылаться на #{uploadSpeed}
а также #{downloadSpeed}
,
Вы не можете сделать IO в файле whamlet. Более того, ваши функции A2 являются IO-действиями, но handlerToWidget требует действия HandlerT, поэтому вам нужно использовать liftIO
конвертировать эти звонки.
Обновление 2
См. http://lpaste.net/169497 для рабочего примера, который делает IO в Виджете.
Вот как я получил это на работу. На самом деле я столкнулся с двумя разными проблемами:
- Делать ввод-вывод внутри виджета
- Ссылка на виджет в
default-layout-wrapper
hamletFile.
Вот решение для ввода-вывода внутри виджета:
nav :: Widget
nav = do
globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
$(whamletFile "templates/navbar.hamlet")
Примечание: подпись типа nav :: Widget
кажется необходимым, иначе механизм вывода типов может запутаться и вывести совершенно другой тип для liftIO
операция (которая изначально происходила со мной).
Что касается второй проблемы, я не мог найти решение для ссылки на виджет в default-layout-wrapper
hamletFile. К тому времени, когда этот конкретный hamletFile рендерится, монада Widget была преобразована в PageContent
типа и теперь ему нужно Html url
тип, чтобы иметь возможность визуализировать его в сочетании с withUrlRenderer
функция. По сути, я не смог получить Widget
а также PageContent
сочинять. Однако следующий подход дал мне желаемый результат по-другому:
default-layout.hamlet: добавлен вызов nav
Виджет в этом файле. Перемещены некоторые элементы из default-layout-wrapper
к этому файлу:
<div .container>
<header>
^{nav}
<div #main role="main">
$maybe msg <- mmsg
<div #message>#{msg}
^{widget}
default-layout-wrapper.hamlet: перенес несколько HTML-элементов из этого файла в default-layout
:
<!-- SNIP -->
<body>
<div class="container">
^{pageBody pc}
<!-- SNIP -->