如何在defaultLayout引用的Widget / Hamlet中执行IO?

时间:2016-07-10 08:08:59

标签: haskell yesod shakespeare-text

我是Yesod的新手,似乎完全失去了Widgets,Handlers,Hamlets,WHamlets以及你有什么!这就是我要做的事情:

  • 我网站上的每个页面都需要一个导航栏,这让我相信实现此目的的正确位置应为defaultLayout
  • 现在,此导航栏需要显示从IO操作中获取的一些信息(它是一个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")

但是,代码拒绝使用一个又一个类型错误进行编译。我已经尝试了很多hametFilewhamletFilehanderToWidgetliftIO的组合,甚至将导航功能放在 {{ 1}},但似乎没有任何效果。据我说,我当前的代码应该编译,但我显然不明白Yesod-Core类型是如何工作的。

我如何让它工作?更重要的是,我误解了什么概念?

修改1:

尝试将defaultLayout功能修改为以下内容:

nav

但是,nav :: Handler Html nav = do globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl $(hamletFile "templates/navbar.hamlet") defaultLayout导致以下类型不匹配:

withUrlRenderer

编辑2:

尝试将 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) 的类型签名更改为:

nav

但是它导致了一个新的类型不匹配,在同一行:

nav :: Widget
nav = do
  globalStat  <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(hamletFile "templates/navbar.hamlet") 

编辑3:

这是 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) 的相关摘录:

-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)

2 个答案:

答案 0 :(得分:1)

查看this SO question的答案。基本上你不能在模板中执行IO。

另请注意,defaultLayout的类型为GHandler ...GHandlerinstance of MonadIO,因此您可以defaultLayout使用liftIO执行IO }}

我会尝试:

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)

所以基本的想法是:

  • 使用defaultLayout
  • liftIO中完成所有IO
  • 将子模板所需的数据作为函数参数传递

<强>更新

要模仿this example in the Yesod book,您需要像这样写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}

你不能在whamlet文件中执行IO。此外,您的A2函数是IO动作,但handlerToWidget需要HandlerT操作,因此您需要使用liftIO转换这些调用。

更新2

有关在Widget中执行IO的工作示例,请参阅http://lpaste.net/169497

答案 1 :(得分:0)

以下是我如何使用它。实际上我遇到了两个不同的问题:

  • 在窗口小部件中执行IO
  • default-layout-wrapper hamletFile。
  • 中引用窗口小部件

这是在小部件中执行IO的解决方案:

nav :: Widget
nav = do
  globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(whamletFile "templates/navbar.hamlet")

注意:类型签名nav :: Widget似乎是必要的,否则类型推断引擎可能会混淆,并为liftIO操作推断出一种非常不同的类型(最初发生在我身上)。

对于第二个问题,我无法真正找到在default-layout-wrapper hamletFile中引用Widget的解决方案。在呈现此特定hamletFile时,Widget monad已转换为PageContent类型,现在需要Html url类型才能将其与withUrlRenderer一起呈现功能。基本上,我无法让WidgetPageContent撰写。但是,以下方法以不同的方式给了我想要的结果:

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 -->