首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在defaultLayout引用的Widget/Hamlet中执行IO?

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

Stack Overflow用户
提问于 2016-07-10 08:08:59
回答 2查看 169关注 0票数 1

我对Yesod很陌生,似乎完全迷上了小部件、处理程序、哈姆雷特、WHamlets等等!我想做的是:

  • 我的站点上的每一个页面都需要一个导航条,这使我相信实现这个功能的正确位置应该是defaultLayout
  • 现在,这个导航条需要显示从IO操作中获得的一些信息(更具体地说,它是一个提供数据的RPC调用)。

因此,我尝试用Foundation.hs编写以下函数(代码布局是基本的yesod-sqlite脚手架模板):

代码语言:javascript
复制
nav = do
  globalStat <- handlerToWidget $ A2.getGlobalStat NWT.ariaRPCUrl
  $(whamletFile "templates/navbar.hamlet)

A2.getGlobalStat :: IO GlobalStatResponse

下面是template/navbar.hamlet的样子:

代码语言:javascript
复制
<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的样子:

代码语言:javascript
复制
<!-- SNIP -->
  <body>
    <div class="container">
      <header>
        ^{nav}
      <div id="main" role="main">
        ^{pageBody pc}
<!-- SNIP -->

下面是defaultLayout的样子:

代码语言:javascript
复制
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的组合,甚至把nav函数放在defaultLayout中,但是似乎没有什么效果。据我所知,我当前的代码应该编译,但我显然不明白Yesod-Core类型是如何工作的。

我怎么才能让这个起作用?更重要的是,我误解了什么概念?

编辑1:

尝试将nav函数修改为:

代码语言:javascript
复制
nav :: Handler Html
nav = do
  globalStat  <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(hamletFile "templates/navbar.hamlet")

但是,这会导致defaultLayout中与withUrlRenderer的类型不匹配

代码语言:javascript
复制
 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的类型签名更改为:

代码语言:javascript
复制
nav :: Widget
nav = do
  globalStat  <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(hamletFile "templates/navbar.hamlet") 

但它导致了一种新的类型错配,同一行:

代码语言:javascript
复制
 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的相关片段

代码语言:javascript
复制
\ _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)

EN

回答 2

Stack Overflow用户

发布于 2016-07-10 13:15:41

看看这个问题的答案。基本上,您不能在模板中执行IO。

还请注意,defaultLayout的类型是GHandler ...GHandlerMonadIO实例,因此可以使用liftIOdefaultLayout中执行IO。

我试着:

代码语言:javascript
复制
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

代码语言:javascript
复制
...
^{nav uploadSpeed downloadSpeed}
...

nav变成了这样的东西:

代码语言:javascript
复制
nav uploadSpeed downloadSpeed =   $(whamletFile "templates/navbar.hamlet)

因此,基本的想法是:

  • defaultLayout中使用liftIO执行所有IO操作
  • 将子模板所需的数据作为函数参数传递。

更新

要模仿Yesod书中的例子,您需要像这样编写navbar

代码语言:javascript
复制
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。而且,您的A2函数是IO操作,但是handlerToWidget需要一个HandlerT操作,所以您需要使用liftIO来转换这些调用。

更新2

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

票数 1
EN

Stack Overflow用户

发布于 2016-07-11 12:27:11

这是我怎么让它工作的。实际上,我面临着两个不同的问题:

  • 在Widget中执行IO
  • 引用default-layout-wrapper hamletFile中的Widget。

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

代码语言:javascript
复制
nav :: Widget
nav = do
  globalStat <- liftIO $ A2.getGlobalStat NWT.ariaRPCUrl
  $(whamletFile "templates/navbar.hamlet")

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

对于第二个问题,我无法真正找到在default-layout-wrapper hamletFile中引用Widget的解决方案。当呈现这个特定的hamletFile时,Widget已经转换为PageContent类型,现在它需要一个Html url类型才能与withUrlRenderer函数一起呈现它。基本上,我无法让WidgetPageContent来作曲。然而,以下方法以不同的方式给了我想要的结果:

default-layout.hamlet:在这个文件中添加了对nav小部件的调用。将一些元素从default-layout-wrapper移到这个文件中:

代码语言:javascript
复制
<div .container>
  <header>
    ^{nav}
  <div #main role="main">
    $maybe msg <- mmsg
      <div #message>#{msg}
    ^{widget}

default-layout-wrapper.hamlet:将一些HTML元素从这个文件移到了default-layout

代码语言:javascript
复制
<!-- SNIP -->
  <body>
    <div class="container">
      ^{pageBody pc}
<!-- SNIP -->
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38289927

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档