Haskell 如何在defaultLayout引用的小部件/小村庄中执行IO?

Haskell 如何在defaultLayout引用的小部件/小村庄中执行IO?,haskell,yesod,shakespeare-text,Haskell,Yesod,Shakespeare Text,我对Yesod是个新手,似乎对小部件、处理程序、哈姆雷特、WHamlets以及你所拥有的东西完全迷茫了!以下是我想做的: 我网站上的每个页面都需要一个导航栏,这让我相信实现这一点的正确位置应该是defaultLayout 现在,这个导航栏需要显示从IO操作获得的一些信息(更具体地说,是一个RPC调用提供了这些数据) 因此,我尝试在Foundation.hs中编写以下函数(代码布局是基本的yesod-sqlitescaffolding模板): 以下是默认布局包装器。hamlet看起来像: &l

我对Yesod是个新手,似乎对小部件、处理程序、哈姆雷特、WHamlets以及你所拥有的东西完全迷茫了!以下是我想做的:

  • 我网站上的每个页面都需要一个导航栏,这让我相信实现这一点的正确位置应该是
    defaultLayout
  • 现在,这个导航栏需要显示从IO操作获得的一些信息(更具体地说,是一个RPC调用提供了这些数据)
因此,我尝试在
Foundation.hs
中编写以下函数(代码布局是基本的
yesod-sqlite
scaffolding模板):

以下是
默认布局包装器。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
<!-- SNIP -->
  <body>
    <div class="container">
      <header>
        ^{nav}
      <div id="main" role="main">
        ^{pageBody pc}
<!-- SNIP -->
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
handerwidget
liftIO
,甚至将导航功能放在
defaultLayout
中,但似乎没有任何效果。据我所知,我当前的代码应该编译,但我显然不了解YesSOD核心类型是如何工作的

我怎样才能让它工作?更重要的是,我误解了什么概念

编辑1:

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

nav :: Handler Html
nav = do
  globalStat  <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(hamletFile "templates/navbar.hamlet")
编辑2:

尝试将
nav
的类型签名更改为:

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

以下是来自
-ddump拼接的相关片段:

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

请查看对的答案。基本上,您不能在模板中执行IO

还要注意的是,
defaultLayout
的类型是
GHandler…
GHandler
是an,因此您可以使用
liftIO
defaultLayout
中执行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")
nav
会变成:

nav uploadSpeed downloadSpeed =   $(whamletFile "templates/navbar.hamlet)
因此,基本思想是:

  • 使用
    liftIO
  • 将子模板所需的数据作为函数参数传递
更新

要进行模拟,您需要像这样编写
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::Widget
navbar=do

globalStat以下是我如何让它工作的。实际上,我面临着两个不同的问题:

  • 在小部件中执行IO
  • 引用
    默认布局包装文件中的小部件
以下是在小部件内执行IO的解决方案:

nav :: Widget
nav = do
  globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(whamletFile "templates/navbar.hamlet")
默认布局包装器。哈姆雷特:将一些HTML元素从此文件移动到
默认布局

<!-- SNIP -->
  <body>
    <div class="container">
      ^{pageBody pc}
<!-- SNIP -->

^{pageBody pc}

谢谢您的回答@ErikR。你会怎么说?我想做的不是很相似吗?谢谢,埃里克。这对我来说仍然不起作用,但至少我已经取得了一些进展。您建议使用
handlerToWidget$liftIO
已使
nav
函数的类型更简单,即
nav::(MonadIO m)=>WidgetT site m()
。然而,这让我想知道为什么首先需要
liftIO
handlerToWidget
。为什么
liftIO
无法将IO操作直接提升到
WidgetT站点m()
转换器堆栈?我的直觉是正确的。实际上并不需要
handlerToWidget
。因为我没有给出顶级类型声明,
liftIO
不知道将IO操作提升到哪个monad。只需添加
nav::Widget
作为顶级类型声明,即可使
nav
函数在编译时不使用
handlerToWidget
。谢谢ErikR。我已经设法在处理程序操作中执行IO,但不是在我所针对的原始代码中。我在您的工作示例中重现了相同的错误,请注意
defaultLayout
实现(这是yesod sqlite scaffold提供的)。一旦
defaultLayout
更改为此定义,
lpaste wrapper.hamlet
就无法调用
^{testWidget2}
^{testWidget}
可能是
-ddump拼接的输出有帮助吗?请看我的最新编辑。编辑我的问题以基于
liftIO
方法发布新的代码片段。现在,问题似乎是
^{nav}
默认布局包装器
模板中的
^{nav}
用法似乎组合不好。
nav uploadSpeed downloadSpeed =   $(whamletFile "templates/navbar.hamlet)
navbar :: Widget
navbar = do
    globalStat <- liftIO A2.getGlobalStat NWT.ariaRPCUrl
    downloadSpeed <- liftIO A2.glDownloadSpeed globalStat
    uploadSpeed <- liftIO A.glUploadSpeed
    $(whamletFile "templates/navbar.hamlet)
nav :: Widget
nav = do
  globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(whamletFile "templates/navbar.hamlet")
<div .container>
  <header>
    ^{nav}
  <div #main role="main">
    $maybe msg <- mmsg
      <div #message>#{msg}
    ^{widget}
<!-- SNIP -->
  <body>
    <div class="container">
      ^{pageBody pc}
<!-- SNIP -->