【问题标题】:In Hakyll, how can I generate a tags page?在 Hakyll 中,如何生成标签页面?
【发布时间】:2018-10-14 17:22:42
【问题描述】:

我正在尝试做类似what's described in this tutorial 的事情,即向我的 Hakyll 博客添加标签,但不是为每个标签生成一个页面,而是只有一个页面列出所有标签及其帖子。所以给Post1标记Tag1,和一个Post2标记Tag1, Tag2,和一个Post3标记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,但它不起作用。)所以我很难考虑如何进行此操作。

The complete file is here on 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

The full code, as it currently stands, is up here.

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

【问题讨论】:

  • the documentation on what Tags are。您将需要使用带有Tags 参数并生成Compiler 或更好的Context 的函数之一,您可以使用它来代替(或一起使用)defaultContext
  • 该文档对我来说真的没有意义,作为一个初学者。我应该使用什么函数来获取我生成的tags,并从中创建一个包含所有标签列表及其所有相关帖子的上下文?
  • 这是一个普通的algebraic data type。您可以使用tagsMap tags 获取元组列表,每个元组都有一个标签名称和一个具有该标签的页面标识符列表。然后阅读TemplateContext documentation,了解如何从中构建listField,您应该能够使用它来呈现标签名称列表。
  • (渲染(标题等)相关帖子本身会更高级,因为标签列表将仅包含帖子标识符。您不会轻易获得该页面的元数据来渲染它,即'将需要额外的努力)。
  • 这究竟会涉及什么?给定标识符,是否有获取页面标题的功能?

标签: haskell hakyll


【解决方案1】:

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

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

{-# LANGUAGE ViewPatterns #-}

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

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

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

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

出于同样的原因,实际上使这些单独的标签页可路由是一个好主意:如果在顶级 Rules monad 中没有此节,则每个帖子上的标签将无法使用默认的 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,因为它想添加一个bodyField,但是这个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 实际操作中:它基本上是说要匹配这个模式,-&gt; 右边的模式(即(_, is))应该匹配应用的结果参数左侧的函数(即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$"

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

我已对您的 repo 进行了 PR,以便您可以在上下文 here 中查看这些更改。

【讨论】:

  • 非常感谢这个非常彻底的回答!除了对我有帮助之外,我认为这对于帮助那些也希望在 Hakyll 中这样做的其他人也大有帮助。
【解决方案2】:

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

Kowainik webpage tags building

以及标签页的例子:

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

你可以问任何关于代码的问题:)

【讨论】:

  • 如果我没记错的话,您的网站似乎每个标签都有一个页面。我可以很好地做到这一点,但我遇到的问题是制作一个列出所有标签的页面,并为每个标签列出所有相关的帖子。
  • @Jono 我们实际上有一个列出所有标签的页面(这是所有帖子页面),但没有显示与之相关的帖子。但是,如果我正确理解您的问题,那么从所有帖子中获取所有标签的技巧可能对您有用。
  • 我可以设法打印出所有标签的列表,并且我可以制作标签页面,每个标签页都列出了该标签的所有相关帖子,但我无法制作一个列出标签的页面他们的相关帖子。我有一种感觉,它涉及创建一个tagsListContext、一个listField 的标签,以及在每个标签中的listFields。但我不明白field 是如何与Item 相关联的,以及它们如何交互。我只是希望我能找到一个列出所有标签的标签页面的工作示例,并为每个标签列出所有相关的帖子。
  • 您能否编辑您的答案以包含一个列出标签的工作代码块,然后在每个标签中列出其相关的帖子?我猜应该是从create ["tags.html"] $ do 开始的。如果是这样,我很乐意给你赏金。
猜你喜欢
  • 2023-04-05
  • 2012-12-01
  • 2018-09-25
  • 1970-01-01
  • 1970-01-01
  • 2014-09-19
  • 2012-02-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多