15

我正在尝试做一些类似于本教程中描述的事情,即向我的 Hakyll 博客添加标签,但不是为每个标签生成一个页面,而是只有一个页面列出所有标签及其帖子。所以给定一个Post1taggedTag1和一个Post2taggedTag1, Tag2和一个Post3tagged Tag2,我的tags.html看起来像这样:

 Tag1: 
  - Post1
  - Post2
 Tag2: 
  - Post2
  - Post3

但我是 Haskell 初学者,我并不完全理解 Hakyll 的所有单子上下文。这是我到目前为止所拥有的:

create ["tags.html"] $ do
    route idRoute
    tags <- buildTags "posts/*" (fromCapture "tags.html")
    compile $
        makeItem ""
            >>= applyTemplate tagListTemplate defaultContext
            >>= applyTemplate defaultTemplate defaultContext
            >>= relativizeUrls
            >>= cleanIndexUrls

Tags问题是,在我的博客中,我真的不知道是什么。我似乎无法将它们打印出来进行调试。(我尝试添加print tags,但它不起作用。)所以我很难考虑如何进行此操作。

完整的文件在 GitHub 上。

任何帮助深表感谢。

更新:我离弄清楚这一点还差得远。这是我现在正在尝试的:

create ["tags.html"] $ do
        route idRoute
        tags <- buildTags "posts/*" (fromCapture "tags.html#")
        let tagList = tagsMap tags
        compile $ do
            makeItem ""
              >>= applyTemplate tagListTemplate (defaultCtxWithTags tags)

随着:

-- Add tags to default context, for tag listing
defaultCtxWithTags :: Tags -> Context String
defaultCtxWithTags tags = listField "tags" defaultContext (return (tagsMap tags)) `mappend` defaultContext

就目前而言,完整的代码在这里。

对此的任何帮助将不胜感激。我知道所有文档,但我似乎无法将其转换为工作代码。

4

2 回答 2

11

我已修改您site.hs以创建一个基本的标签列表页面,我认为该页面具有所需的结构:标签列表,每个标签都包含带有该标签的帖子列表。

以下是我必须做的每件事的总结:

{-# LANGUAGE ViewPatterns #-}

不是绝对必要的,但是我曾经使用过的一个很好的语言扩展。我想我会使用/提到它,因为你提到你是 Haskell 的初学者,很高兴知道。

tags <- buildTags "posts/*" (fromCapture "tags/*.html")

buildTags您最初的site.hs. 一是它可能应该从单个match子句移到顶级Rulesmonad 中,以便我们可以在需要时创建单个标签页。另一个是捕获同样从 更改"tags.html#""tags/*.html"。这很重要,因为 Hakyll 希望每个标签页Item都有一个唯一的Identifier,而不是每个标签页都是相同的。

拥有具有唯一标识符的单个标签页面可能不是绝对必要的,但可以简化其余设置,因为许多 Hakyll 机器都假设它们存在。特别是,Tags:各个帖子描述中的行以前也没有正确呈现。

出于同样的原因,实际上使这些单独的标签页面可路由是一个好主意:如果在顶级Rulesmonad 中没有此节,则每个帖子上的标签将无法使用您使用的默认值正确呈现tagsField,因为它们不能弄清楚如何链接到单个标签页面:

tagsRules tags $ \tag pat -> do
    route idRoute
    compile $ do
        posts <- recentFirst =<< loadAll pat
        let postCtx = postCtxWithTags tags
            postsField = listField "posts" postCtx (pure posts)
            titleField = constField "title" ("Posts tagged \""++tag++"\"")
            indexCtx = postsField <> titleField <> defaultContext
        makeItem "" >>= applyTemplate postListTemplate indexCtx
                    >>= applyTemplate defaultTemplate defaultContext
                    >>= relativizeUrls
                    >>= cleanIndexUrls

好了,这就是预赛。现在进入主要景点:

defaultCtxWithTags tags = listField "tags" tagsCtx getAllTags         `mappend`
                          defaultContext

好的,这里添加的重要内容是一些tags字段。它将包含由 返回的每个事物的一个项目getAllTags,并且每个项目上的字段将由 给出tagsCtx

  where getAllTags :: Compiler [Item (String, [Identifier])]
        getAllTags = pure . map mkItem $ tagsMap tags
          where mkItem :: (String, [Identifier]) -> Item (String, [Identifier])
                mkItem x@(t, _) = Item (tagsMakeId tags t) x

在做什么getAllTags?好吧,它以 开头tagsMap tags,就像您的示例一样。但是 Hakyll 希望结果是Item,所以我们必须使用mkItem. Item除了身体还有什么?只是一个Identifier,Tags对象恰好包含一个告诉我们如何获取它的字段!因此mkItem,只需使用tagsMakeId来获取标识符并用该标识符包装给定的主体。

关于什么tagsCtx?

        tagsCtx :: Context (String, [Identifier])
        tagsCtx = listFieldWith "posts" postsCtx getPosts             `mappend`
                  metadataField                                       `mappend`
                  urlField "url"                                      `mappend`
                  pathField "path"                                    `mappend`
                  titleField "title"                                  `mappend`
                  missingField

开始的一切metadataField只是我们期望从中得到的通常的东西defaultContext;我们不能defaultContext在这里使用,因为它想添加 a bodyField,但是 this 的主体Item不是字符串(而是对我们来说更有用的 Haskell 结构表示标签)。有趣的是添加posts字段的行,看起来应该有点熟悉。最大的区别在于它使用listFieldWith了代替listField,这基本上意味着getPosts获得了一个额外的参数,这是Item该字段所在的主体。在这种情况下,这是来自 的标签记录tagsMap

          where getPosts :: Item (String, [Identifier])
                         -> Compiler [Item String]
                getPosts (itemBody -> (_, is)) = mapM load is

getPosts大多数情况下,它只是使用该load功能来获取Item每个帖子的Identifier--- 这很像loadAll您在索引页面上获取所有帖子的方法,但它只给您一个帖子。左边看起来怪异的模式匹配正在起作用ViewPatterns:它基本上是说要匹配这个模式,->(ie (_, is)) 右边的模式应该与将左边 (ie itemBody) 的函数应用到争论。

                postsCtx :: Context String
                postsCtx = postCtxWithTags tags

postsCtx非常简单:在postCtxWithTags我们渲染帖子的其他任何地方都使用相同的方法。

这是获得所需的一切所需的Context一切;剩下的就是实际制作一个模板来渲染它!

tagListTemplateRaw :: Html
tagListTemplateRaw =
  ul $ do
    "$for(tags)$"
    li ! A.class_ "" $ do
      a ! href "$url$" $ "$title$"
      ul $ do
        "$for(posts)$"
        li ! A.class_ "" $ do
          a ! href "$url$" $ "$title$"
        "$endfor$"
    "$endfor$"

这只是一个呈现嵌套列表的非常简单的模板;你当然可以做各种各样的事情来让它看起来更漂亮/更好看。

我已对您的存储库进行了 PR,以便您可以在此处查看上下文中的这些更改。

于 2018-11-22T00:47:49.150 回答
1

以下是我们为在网页上实现此行为所做的工作:

Kowainik 网页标签构建

以及标签页的例子:

https://kowainik.github.io/tags/haskell

您可以询问有关代码的任何问题:)

于 2018-11-01T09:10:05.393 回答