I'm new to Yesod and seem to be completely lost with Widgets, Handlers, Hamlets, WHamlets, and what have you! Here's what I'm trying to do:
- Every page on my site needs to have a navbar, which leads me to believe that the correct place for implementing this should be
defaultLayout - Now, this navbar needs to display some information that is obtained from an IO action (it's an RPC call which gives this data, to be more specific).
Therefore, I tried writing the following function in Foundation.hs (the code layout is the basic yesod-sqlite scaffolding template):
nav = do
globalStat <- handlerToWidget $ A2.getGlobalStat NWT.ariaRPCUrl
$(whamletFile "templates/navbar.hamlet)
A2.getGlobalStat :: IO GlobalStatResponse
Here's what template/navbar.hamlet looks like:
<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
Here's what default-layout-wrapper.hamlet looks like:
<!-- SNIP -->
<body>
<div class="container">
<header>
^{nav}
<div id="main" role="main">
^{pageBody pc}
<!-- SNIP -->
Here's what defaultLayout looks like:
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")
However, the code refuses to compile with one type-error after another. I've tried a lot of combinations of hametFile, whamletFile, handerToWidget, liftIO, even placing the nav function inside defaultLayout, but nothing seems to work. According to me my current code should compile, but I've obviously not understood how the Yesod-Core types are working.
How do I get this to work? And more importantly, what concept have I misunderstood?
Edit 1:
Have tried modifying the nav function to the following:
nav :: Handler Html
nav = do
globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
$(hamletFile "templates/navbar.hamlet")
But, it results in the following type mismatch in defaultLayout on the line with 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)
Edit 2:
Tried changing the type signature of nav to:
nav :: Widget
nav = do
globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
$(hamletFile "templates/navbar.hamlet")
But it results in a new type-mismatch, in the same line:
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)
Edit 3:
Here's a relevant snippet from -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 }
The type of (pageHead pc) and (pageBody pc) is HtmlUrl (Route App)
Have a look at the answer to this SO question. Basically you can't perform IO in a template.
Also note that the type of
defaultLayoutisGHandler ...andGHandleris an instance of MonadIO, so you can perform IO indefaultLayoutby usingliftIO.I would try:
And in
templates/default-layout-wrapper.hamlet:And
navbecomes something like:So the basic ideas are:
defaultLayoutusingliftIOUpdate
To emulate this example in the Yesod book you need to write
navbarlike this:And in
navbar.whamletrefer to#{uploadSpeed}and#{downloadSpeed}.You can't do IO in a whamlet file. Moreover, your A2 functions are IO-actions, but handlerToWidget requires a HandlerT action so you need to use
liftIOto convert those calls.Update 2
See http://lpaste.net/169497 for a working example which does IO in a Widget.