我正在尝试做类似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.
任何对此的帮助将不胜感激。我知道所有文档,但似乎无法将其转换为工作代码。
答案 0 :(得分:3)
我已经修改了您的site.hs
,以创建一个基本的标签列表页面,我认为该页面具有所需的结构:标签列表,每个标签列表都包含带有该标签的帖子。
以下是我要使其正常工作必须做的每件事的摘要:
{-# LANGUAGE ViewPatterns #-}
不是严格必需的,但是我曾经使用过一个不错的语言扩展。我以为我会使用/提及它,因为您提到您是Haskell的初学者,很高兴知道。
tags <- buildTags "posts/*" (fromCapture "tags/*.html")
与初始buildTags
中的site.hs
相比,此行需要进行两项更改。一种是它可能应该从单个match
子句中移出到顶级Rules
monad中,以便我们可以根据需要创建单个标记页。另一个是捕获类似地从"tags.html#"
更改为"tags/*.html"
。这很重要,因为Hakyll希望每个Item
都具有唯一的Identifier
,并且并非每个标签页面都是相同的。
具有唯一标识符的各个标签页可能不是严格必要的,但是由于许多Hakyll机器都假定它们存在,因此简化了其余设置。特别是,各个帖子描述中的Tags:
行以前也没有正确显示。
出于相同的原因,最好使这些单独的标签页可路由:不使用顶级Rules
monad中的该节,则每个帖子上的标签都无法使用默认的{您使用的{1}},因为他们不知道如何链接到单个标签页:
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
开头,就像您的示例一样。但是Hakyll希望结果为tagsMap tags
,因此我们必须使用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
的主体不是字符串(而是对我们的Haskell结构表示有用,标签)。有趣的是,这行添加了Item
字段,这看起来有些熟悉。最大的区别是它使用posts
而不是listFieldWith
,这基本上意味着listField
得到了一个额外的参数,它是该字段所在的getPosts
的主体。在这种情况下,这就是Item
的标签记录。
tagsMap
where getPosts :: Item (String, [Identifier])
-> Compiler [Item String]
getPosts (itemBody -> (_, is)) = mapM load is
通常仅使用getPosts
函数来获取给定load
的每个帖子的Item
,这与{{1} },您可以在索引页面上获取所有帖子,但这只为您提供了一篇帖子。左边的模式匹配看起来很奇怪,实际上是Identifier
:基本上是说,要匹配此模式,loadAll
(即ViewPatterns
)右边的模式应该将应用左侧函数(即->
)的结果与参数匹配。
(_, is)
itemBody
非常简单:就像我们在其他地方渲染帖子一样使用的 postsCtx :: Context String
postsCtx = postCtxWithTags tags
。
这是使postsCtx
与您想要的所有内容所必需的一切;剩下的就是实际制作一个模板来渲染它!
postCtxWithTags
这只是一个非常简单的模板,用于呈现嵌套列表;当然,您可以做各种事情使它看起来更漂亮/更黑。
我已对您的仓库进行了PR,以便您可以在上下文here中看到这些更改。
答案 1 :(得分:1)
这是我们为实现此行为而在网页上所做的事情:
Kowainik webpage tags building
以及标记页的示例:
https://kowainik.github.io/tags/haskell
您可以询问有关代码的任何问题:)