2016-07-10 6 views
1

Я новичок в Yesod и, кажется, полностью потерян с помощью виджетов, Handlers, Hamlets, Whamlets и что у вас есть! Вот что я пытаюсь сделать:Как сделать IO в виджете/Гамлете, на который ссылается defaultLayout?

  • Каждая страница на моем сайте должна иметь Navbar, что приводит меня к мысли, что правильное место для реализации этого должна быть defaultLayout
  • Теперь этот Navbar нужно отобразить некоторую информацию, полученную от действия ввода-вывода (это скорее вызов 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, даже если разместить функцию nav внутриdefaultLayout, но ничего не работает. По моему мнению, мой текущий код должен скомпилировать, но я, очевидно, не понял, как работают типы Yesod-Core.

Как я могу заставить это работать? И что еще более важно, какую концепцию я неправильно понял?

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

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

+0

Отредактированным мой вопрос к сообщению нового кода фрагменты, основанные на подходе «liftIO». Теперь проблема заключается в том, что использование '^ {nav}' в шаблоне 'default-layout-wrapper', похоже, не очень хорошо составлено. –

ответ

1

Посмотрите ответ на this SO question. В принципе, вы не можете выполнять IO в шаблоне.

Также обратите внимание, что тип defaultLayout является GHandler ... и GHandler является instance of 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
  • передачи данных, необходимых вспомогательных шаблонов в качестве аргументов функции

Update

Для эмуляции 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}.

Вы не можете выполнить ввод-вывод в файле-визитке. Кроме того, ваши функции A2 являются IO-действиями, но для handlerToWidget требуется действие HandlerT, поэтому вам необходимо использовать liftIO для преобразования этих вызовов.

Update 2

См http://lpaste.net/169497 для рабочего примера, который делает IO в виджете.

+0

Спасибо за ответ @ErikR. Что вы скажете о http://www.yesodweb.com/book/yesods-monads#yesods-monads_example_database_driven_navbar? Разве я не пытаюсь сделать подобное? –

+1

Ответ обновлен. – ErikR

+0

Спасибо, ErikR. он все еще не работает для меня, но, по крайней мере, я добился определенного прогресса. Ваше предложение об использовании 'handlerToWidget $ liftIO' привело к более простому типу для функции' nav', т. Е. 'Nav :: (MonadIO m) => WidgetT site m()'. Тем не менее, это заставляет меня задаться вопросом, почему 'liftIO' необходимо« handlerToWidget »в первую очередь. Почему «liftIO» не смог поднять действие ввода-вывода непосредственно на стек трансформатора «widgetT» m()? –

0

Вот как я получил это, чтобы работать. Были фактически две разные проблемы, я столкнулся:

  • Doing IO внутри Widget
  • REFERENCING виджет в 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 отображается, монашка Виджета была преобразована в тип PageContent, и теперь ему нужен тип Html url, чтобы иметь возможность отображать его в сочетании с функцией withUrlRenderer. В принципе, я не смог получить Widget и PageContent. Однако следующий подход дал мне желаемый результат, по-другому:

по умолчанию-layout.hamlet: Добавлена ​​взывание к nav виджета в этом файле. Переехал некоторые элементы из default-layout-wrapper в этот файл:

<div .container> 
    <header> 
    ^{nav} 
    <div #main role="main"> 
    $maybe msg <- mmsg 
     <div #message>#{msg} 
    ^{widget} 

по умолчанию макет-wrapper.hamlet: Перемещенных несколько HTML-элемента из этого файла в default-layout:

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