向 Gatsby Contentful 博客添加标签
Adding tags to Gatsby Contentful blog
我正在尝试在博文中添加标签,但我很难找到解释如何实现它们的资源。
最终目标是获得可点击的标签,这会导致一个页面,其中所有具有相同标签的帖子都显示在列表中。
我正在使用带有 Contentful 集成的 GatsbyJS。
我有一个名为 article-post.tsx 的文件,其中包含以下代码:
import React from "react"
import { graphql } from "gatsby"
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"
import Layout from "../components/layout/layout"
import Img from "gatsby-image"
import SEO from "../components/layout/seo"
import styled from "styled-components"
import { BodyMain, H1 } from "../components/styles/TextStyles"
export const query = graphql`
query($slug: String!) {
contentfulArticlePost(slug: { eq: $slug }) {
title
tags
publishedDate(formatString: "Do MMMM, YYYY")
featuredImage {
fluid(maxWidth: 720) {
...GatsbyContentfulFluid
}
}
body {
json
}
}
}
`
const ArticlePost = props => {
const options = {
renderNode: {
"embedded-asset-block": node => {
const alt = node.data.target.fields.title["en-US"]
const url = node.data.target.fields.file["en-US"].url
return <img alt={alt} src={url} className="embeddedImage" />
},
},
}
return (
<Layout>
<SEO title={props.data.contentfulArticlePost.title} />
<Wrapper>
<ImageWrapper>
{props.data.contentfulArticlePost.featuredImage && (
<Img
className="featured"
fluid={props.data.contentfulArticlePost.featuredImage.fluid}
alt={props.data.contentfulArticlePost.title}
/>
)}
</ImageWrapper>
<Title>{props.data.contentfulArticlePost.title}</Title>
<Tags>
{props.data.contentfulArticlePost.tags.map(tag => (
<span className="tag" key={tag}>
{tag}
</span>
))}
</Tags>
<ContentWrapper>
{documentToReactComponents(
props.data.contentfulArticlePost.body.json,
options
)}
</ContentWrapper>
</Wrapper>
</Layout>
)
}
export default ArticlePost
const Wrapper = styled.div`
display: grid;
grid-gap: 1.875rem;
margin: 0 auto;
padding: 7rem 1.875rem;
max-width: 900px;
`
const ImageWrapper = styled.div`
.featured {
border-radius: 15px;
}
`
const Title = styled(H1)`
margin: 0 auto;
text-align: center;
`
const Tags = styled.div`
margin: 0 auto;
.tag {
background: #8636E4;
border-radius: 1rem;
padding: 0.5rem;
margin: 0.2rem;
font-weight: 600;
}
`
const ContentWrapper = styled(BodyMain)`
display: grid;
grid-gap: 20px;
max-width: 900px;
margin: 0 auto;
line-height: 1.6;
.embeddedImage {
padding: 50px 0px;
width: 100%;
height: auto;
}
`
它确实给了我标签,我可以为它们设置样式。虽然我不知道如何让它们像 links/buttons 一样可点击。
我有一个名为 gatsby-node.js 的文件,其中包含以下代码:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const response = await graphql(`
query {
allContentfulArticlePost {
edges {
node {
id
slug
}
}
}
}
`)
response.data.allContentfulArticlePost.edges.forEach(edge => {
createPage({
path: `/articles/${edge.node.slug}`,
component: path.resolve("./src/templates/article-post.tsx"),
context: {
slug: edge.node.slug,
id: edge.node.id
},
})
})
}
我从这里去哪里?
首先,您需要为每个标签创建动态页面以创建有效的link元素。在您的 gatsby-node.js
中创建一个查询以获取所有标签并为每个标签创建页面,例如:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const response = await graphql(`
query {
allContentfulArticlePost {
edges {
node {
id
slug
}
}
}
}
`)
response.data.allContentfulArticlePost.edges.forEach(edge => {
createPage({
path: `/articles/${edge.node.slug}`,
component: path.resolve("./src/templates/article-post.tsx"),
context: {
slug: edge.node.slug,
id: edge.node.id
},
})
})
const tags= await graphql(`
query {
allContentfulArticlePost {
edges {
node {
tags
}
}
}
}
`)
tags.data.allContentfulArticlePost.edges.forEach(edge=> {
let slugifiedTag= edges.node.tag.toLowerCase().replace("/^\s+$/g", "-");
createPage({
path: `/tag/${slugifiedTag}`,
component: path.resolve("./src/templates/tag-post.tsx"), // your tagComponent
context: {
slug: edge.node.slug,
tagName: edges.node.tag
},
})
})
}
一步一步,首先,您需要从 tags
查询中的每个博客中检索所有标签。
然后,对于每个标签,您需要根据名称创建一个有效的 slug(即:This Is a Sample Tag
将在示例中转换为 this-is-a-sample-tag
、slugifiedTag
)。这是在 edges.node.tag.toLowerCase().replace("/^\s+$/g", "-")
中完成的,正则表达式将全局匹配所有空格,并将用连字符 replace("/^\s+$/g", "-")
替换它们。您可能需要解析 tags
边缘以删除重复项以避免创建重复条目,创建 Set
应该适合您。
此时,您将在 /tag/${slugifiedTag
}(即:/tag/this-is-a-sample-tag
)下创建所有页面。因此,您需要将 article-post.tsx
更改为指向标签页:
<Tags>
{props.data.contentfulArticlePost.tags.map(tag => {
let slugifiedTag= edges.node.tag.toLowerCase().replace("/^\s+$/g", "-");
return <Link className="tag" key={tag} to={slugifiedTag}>
{tag}
</Link>
})}
</Tags>
请注意,您正在重复 slugifiedTag
函数。您可以通过在 CMS 中创建标签实体并添加 name
和 slug
值来避免这种情况。如果您在 gatsby-node.js
查询和模板查询中检索 slug,您可以直接指向 <Link className="tag" key={tag} to={tag.slug}>
。按照这个例子,name
将是 This is a Sample Tag
而 slug
将是直接 this-is-a-sample-tag
.
您最后要做的是在您的 tag-post.tsx
中创建一个查询,以获取每个标签的所有帖子,因为您是通过上下文传递 slug
和 tagName
。您的查询应如下所示:
export const query = graphql`
query($slug: String!, $tags: [String!]) {
contentfulArticlePost(slug: { eq: $slug }, tags: { in: $tags}) {
title
tags
publishedDate(formatString: "Do MMMM, YYYY")
featuredImage {
fluid(maxWidth: 720) {
...GatsbyContentfulFluid
}
}
body {
json
}
}
}
`
因为$tags
是一个数组,应该声明为[String!]
。如果要使字段不可为空,只需添加感叹号 (!
),如 [String!]!
。那么你只需要过滤包含至少一个标签的by标签:tags: { in: $tags})
.
正如我所说,这应该通过在您的 CMS 中添加带有 name
和 slug
字段的标签实体来改进和简化。
在不了解您的数据结构和内部组件的情况下,这是一个广泛的问题,但您可以了解该方法的主要思想。
我正在尝试在博文中添加标签,但我很难找到解释如何实现它们的资源。
最终目标是获得可点击的标签,这会导致一个页面,其中所有具有相同标签的帖子都显示在列表中。
我正在使用带有 Contentful 集成的 GatsbyJS。
我有一个名为 article-post.tsx 的文件,其中包含以下代码:
import React from "react"
import { graphql } from "gatsby"
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"
import Layout from "../components/layout/layout"
import Img from "gatsby-image"
import SEO from "../components/layout/seo"
import styled from "styled-components"
import { BodyMain, H1 } from "../components/styles/TextStyles"
export const query = graphql`
query($slug: String!) {
contentfulArticlePost(slug: { eq: $slug }) {
title
tags
publishedDate(formatString: "Do MMMM, YYYY")
featuredImage {
fluid(maxWidth: 720) {
...GatsbyContentfulFluid
}
}
body {
json
}
}
}
`
const ArticlePost = props => {
const options = {
renderNode: {
"embedded-asset-block": node => {
const alt = node.data.target.fields.title["en-US"]
const url = node.data.target.fields.file["en-US"].url
return <img alt={alt} src={url} className="embeddedImage" />
},
},
}
return (
<Layout>
<SEO title={props.data.contentfulArticlePost.title} />
<Wrapper>
<ImageWrapper>
{props.data.contentfulArticlePost.featuredImage && (
<Img
className="featured"
fluid={props.data.contentfulArticlePost.featuredImage.fluid}
alt={props.data.contentfulArticlePost.title}
/>
)}
</ImageWrapper>
<Title>{props.data.contentfulArticlePost.title}</Title>
<Tags>
{props.data.contentfulArticlePost.tags.map(tag => (
<span className="tag" key={tag}>
{tag}
</span>
))}
</Tags>
<ContentWrapper>
{documentToReactComponents(
props.data.contentfulArticlePost.body.json,
options
)}
</ContentWrapper>
</Wrapper>
</Layout>
)
}
export default ArticlePost
const Wrapper = styled.div`
display: grid;
grid-gap: 1.875rem;
margin: 0 auto;
padding: 7rem 1.875rem;
max-width: 900px;
`
const ImageWrapper = styled.div`
.featured {
border-radius: 15px;
}
`
const Title = styled(H1)`
margin: 0 auto;
text-align: center;
`
const Tags = styled.div`
margin: 0 auto;
.tag {
background: #8636E4;
border-radius: 1rem;
padding: 0.5rem;
margin: 0.2rem;
font-weight: 600;
}
`
const ContentWrapper = styled(BodyMain)`
display: grid;
grid-gap: 20px;
max-width: 900px;
margin: 0 auto;
line-height: 1.6;
.embeddedImage {
padding: 50px 0px;
width: 100%;
height: auto;
}
`
它确实给了我标签,我可以为它们设置样式。虽然我不知道如何让它们像 links/buttons 一样可点击。
我有一个名为 gatsby-node.js 的文件,其中包含以下代码:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const response = await graphql(`
query {
allContentfulArticlePost {
edges {
node {
id
slug
}
}
}
}
`)
response.data.allContentfulArticlePost.edges.forEach(edge => {
createPage({
path: `/articles/${edge.node.slug}`,
component: path.resolve("./src/templates/article-post.tsx"),
context: {
slug: edge.node.slug,
id: edge.node.id
},
})
})
}
我从这里去哪里?
首先,您需要为每个标签创建动态页面以创建有效的link元素。在您的 gatsby-node.js
中创建一个查询以获取所有标签并为每个标签创建页面,例如:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const response = await graphql(`
query {
allContentfulArticlePost {
edges {
node {
id
slug
}
}
}
}
`)
response.data.allContentfulArticlePost.edges.forEach(edge => {
createPage({
path: `/articles/${edge.node.slug}`,
component: path.resolve("./src/templates/article-post.tsx"),
context: {
slug: edge.node.slug,
id: edge.node.id
},
})
})
const tags= await graphql(`
query {
allContentfulArticlePost {
edges {
node {
tags
}
}
}
}
`)
tags.data.allContentfulArticlePost.edges.forEach(edge=> {
let slugifiedTag= edges.node.tag.toLowerCase().replace("/^\s+$/g", "-");
createPage({
path: `/tag/${slugifiedTag}`,
component: path.resolve("./src/templates/tag-post.tsx"), // your tagComponent
context: {
slug: edge.node.slug,
tagName: edges.node.tag
},
})
})
}
一步一步,首先,您需要从 tags
查询中的每个博客中检索所有标签。
然后,对于每个标签,您需要根据名称创建一个有效的 slug(即:This Is a Sample Tag
将在示例中转换为 this-is-a-sample-tag
、slugifiedTag
)。这是在 edges.node.tag.toLowerCase().replace("/^\s+$/g", "-")
中完成的,正则表达式将全局匹配所有空格,并将用连字符 replace("/^\s+$/g", "-")
替换它们。您可能需要解析 tags
边缘以删除重复项以避免创建重复条目,创建 Set
应该适合您。
此时,您将在 /tag/${slugifiedTag
}(即:/tag/this-is-a-sample-tag
)下创建所有页面。因此,您需要将 article-post.tsx
更改为指向标签页:
<Tags>
{props.data.contentfulArticlePost.tags.map(tag => {
let slugifiedTag= edges.node.tag.toLowerCase().replace("/^\s+$/g", "-");
return <Link className="tag" key={tag} to={slugifiedTag}>
{tag}
</Link>
})}
</Tags>
请注意,您正在重复 slugifiedTag
函数。您可以通过在 CMS 中创建标签实体并添加 name
和 slug
值来避免这种情况。如果您在 gatsby-node.js
查询和模板查询中检索 slug,您可以直接指向 <Link className="tag" key={tag} to={tag.slug}>
。按照这个例子,name
将是 This is a Sample Tag
而 slug
将是直接 this-is-a-sample-tag
.
您最后要做的是在您的 tag-post.tsx
中创建一个查询,以获取每个标签的所有帖子,因为您是通过上下文传递 slug
和 tagName
。您的查询应如下所示:
export const query = graphql`
query($slug: String!, $tags: [String!]) {
contentfulArticlePost(slug: { eq: $slug }, tags: { in: $tags}) {
title
tags
publishedDate(formatString: "Do MMMM, YYYY")
featuredImage {
fluid(maxWidth: 720) {
...GatsbyContentfulFluid
}
}
body {
json
}
}
}
`
因为$tags
是一个数组,应该声明为[String!]
。如果要使字段不可为空,只需添加感叹号 (!
),如 [String!]!
。那么你只需要过滤包含至少一个标签的by标签:tags: { in: $tags})
.
正如我所说,这应该通过在您的 CMS 中添加带有 name
和 slug
字段的标签实体来改进和简化。
在不了解您的数据结构和内部组件的情况下,这是一个广泛的问题,但您可以了解该方法的主要思想。