我已修改您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 中没有此节,则每个帖子上的标签将无法使用您使用的默认值正确呈现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,以便您可以在此处查看上下文中的这些更改。