Where to define variables in GraphQL - reactjs

Excuse this newbie here. I want to list some articles based on their categories, now I have categories page where you click a category, a list of articles under that specific category should open. The question is, where to define the variable for the slug [which is equal to the article's category].
Where to define $slug variable which is equal to the category, given that I come from categories page, this must be a post request which sends the clicked category, I should put it somewhere on that page, can someone guide me if I make any sense here?!
Thanks in advance.
const EduCatTemp = ({data}) => {
const {allStrapiEducations:{nodes:educations}} = data
return (
<Layout>
{
educations.map((education)=> {
return (
<p>{education.title}</p>
)
})
}
</Layout>
)
}
export default EduCatTemp
export const query = graphql`
{
allStrapiEducations(filter: {education_category: {slug: {eq: $slug}}}) {
nodes {
title
}
}
}
Here is my gatsby-node.js file
educations: allStrapiEducations {
nodes {
slug
}
}
education_categories: allStrapiEducationCategories {
nodes {
slug
}
}
result.data.educations.nodes.forEach(education => {
createPage({
path: `/education/${education.slug}`,
component: path.resolve(`src/templates/education-template.js`),
context: {
slug: education.slug,
},
})
})
result.data.education_categories.nodes.forEach(educat => {
createPage({
path: `/education/${educat.slug}`,
component: path.resolve(`src/templates/education-category-template.js`),
context: {
slug: educat.slug,
},
})
})
Here is the [parent] education page which I want to take the slug from
import React from 'react'
import Layout from '../components/layout'
import EducationCard from '../components/EducationComponent/EducationCard'
import Category from '../components/utilities/Category'
const Education = ({data}) => {
const {allStrapiEducationCategories:{nodes:educations}} = data
return (
<Layout>
<div className="p-5">
<h1 className="sm:text-3xl text-2xl font-medium title-font text-gray-900 headline py-10">Beekeeping Programs</h1>
<input type="search" id="gsearch" placeholder="Search..." name="gsearch" className="my-5 py-2 search-button lg:w-1/3" />
<div className="flex flex-wrap mx-auto">
{educations.map((education)=> {
return <EducationCard headline={education.name} image={education.picture.childImageSharp.fixed} slug={`/education/${education.slug}`} />
})}
</div>
</div>
</Layout>
)
}
export default Education
export const query = graphql`
{
allStrapiEducationCategories {
nodes {
slug
picture {
childImageSharp {
fixed(width: 400
height: 200) {
...GatsbyImageSharpFixed
}
}
}
name
}
}
}
`

I somehow could reolve the issue, which was editing the Graphql query in a slightly different way, given that I had gatsby-node.js set up correctly.
export const query = graphql`
query getSingleNewsCategory($slug: String!)
{
strapiNewsCategories(slug: { eq: $slug }) {
name
}
allStrapiIndustries(
filter: {news_category: {slug: {eq: $slug}}}
) {
nodes {
report_photo {
childImageSharp {
fluid {
src
}
}
}
quote
content
title
slug
minutes_read
date(formatString: "MM")
article_author {
name
}
}
}
allStrapiNewsCategories {
nodes {
slug
name
}
}
}
`

Related

Problem to get an image in a blogpost with NextJS and WpGraphQL

I have another question in the project I'm doing, using NextJS to create a site with a blog part in Wordpress, through WPGraphQL, and I need support in a specific part. Let's go...
I managed to pull the highlighted image with almost no problems, I broke my head a bit, but it worked. The result that's functioning is the post excerpt, the code and the query were as follows (in time: image merely to test, it was the first idea that came to my mind, the site is not about Pokemon):
Image with working image in the excerpt, codes below
NextJS code:
import { LastPosts, PostContainer } from "./Styled";
const RecentBlogPosts = ({lastPosts}) => {
const posts = lastPosts;
return (
<LastPosts>
<h1> ÚLTIMAS POSTAGENS </h1>
{posts?.map((post) => {
return (
<PostContainer key={post.slug}>
<img src={post.featuredImage?.node.sourceUrl} alt="" />
<Link href={`/post/${post.slug}`}>
<a>
<h3> { post.title } </h3>
<div dangerouslySetInnerHTML={{ __html: post.excerpt }} />
<button> Saiba mais </button>
</a>
</Link>
</PostContainer>
)
})}
</LastPosts>
)
};
export default RecentBlogPosts;
Query for this part:
export const RECENT_POSTS = `query RecentPosts {
posts(where: {orderby: {field: DATE, order: DESC}}, first: 2) {
nodes {
id
slug
title
excerpt
featuredImage {
node {
sourceUrl
}
}
}
}
}`;
But I tried to pull the same image in the full blogpsot and it wasn't working... It appears when I view the post from the generic WordPress admin template, but not at my NextJS site, which i See through localhost:3000/post/[here would be the post title in slug] that I'm using. The rest is normal, text with all fonts and specifications with styled components, as well as tags, they work without any problem. The following is the same schema: image with result, code and query that I am using, this time for the part where I'm having problems:
Image with blogpost not working, codes below
NextJS code:
import fetcher from "../../lib/fetcher";
import { GET_ALL_POSTS_WITH_SLUG, POST_BY_SLUG } from "../../lib/wordpress/api";
import { useRouter } from "next/router";
import { Reset } from "../../constants/StyledConstants";
import Header from "../../components/Header/Header";
import { BlogArticle, BlogPostContent, TagLinks, TagWrapper } from "./StyledPost";
import Footer from "../../components/Footer/Footer";
const post = ({ postData }) => {
const blogPost = postData.data.post;
console.log(postData);
const tags = postData.data.post.tags.nodes;
const router = useRouter;
if(!router.isFallback && !blogPost?.slug) {
return <div>erro</div>
}
return (
<>
<Reset />
<Header />
<BlogPostContent>
{router.isFallback ? (
<div> Carregando...... </div>
) : (
<div>
<h1> { blogPost.title } </h1>
<img src={post.featuredImage?.node.sourceUrl} alt="imagem não aparece" />
<BlogArticle dangerouslySetInnerHTML={{ __html: blogPost.content }} />
<TagWrapper>
{tags.map((tag) => <TagLinks href={`/tags/${tag.slug}`} key={tag.slug}> { tag.name } </TagLinks>)}
</TagWrapper>
</div>
)}
</BlogPostContent>
<Footer />
</>
)
}
export default post;
export async function getStaticPaths() {
const response = await fetcher(GET_ALL_POSTS_WITH_SLUG);
const allposts = await response.data.posts.nodes;
return {
paths: allposts.map((post) => `/post/${post.slug}`) || [],
fallback: false
};
}
export async function getStaticProps({ params }) {
const variables = {
id: params.slug,
idType: "SLUG"
};
const data = await fetcher(POST_BY_SLUG, { variables })
return {
props: {
postData: data
},
};
}
Query being used:
export const POST_BY_SLUG = `query PostBySlug($id: ID!, $idType: PostIdType!) {
post(id: $id, idType: $idType) {
title
slug
date
content
featuredImage {
node {
sourceUrl
}
}
tags {
nodes {
name
slug
}
}
}
}`;
I tried to use {post.featuredImage?.node.sourceUrl} because, as far as I understand, following the same basis I did for the excerpt in the blogspot, it should work, but I guess I was wrong... I tried to think of other ways to do it to get to the image, without success... Could someone help to point out where I am wrong please? Thank you very much in advance!!

Displaying all blog posts with Gatsby Contentful

Im trying to display all my Contentful blog posts to my index page in Gatsby but i get an error.
im creating the Posts pages on gatsby-node.js like this:
const path = require(`path`)
// Log out information after a build is done
exports.onPostBuild = ({ reporter }) => {
reporter.info(`Your Gatsby site has been built!`)
}
// Create blog pages dynamically
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const blogPostTemplate = path.resolve(`src/templates/blogPost.js`)
const result = await graphql(`
query {
allContentfulPost {
edges {
node {
postTitle
slug
}
}
}
}
`)
result.data.allContentfulPost.edges.forEach(edge => {
createPage({
path: `${edge.node.slug}`,
component: blogPostTemplate,
context: {
title: edge.node.postTitle,
slug: edge.node.slug,
},
})
})
}
based on this template:
import React from "react"
import { graphql } from "gatsby"
import styled from "styled-components"
export const pageQuery = graphql`
query($slug: String!) {
post: contentfulPost(slug: { eq: $slug }) {
slug
postTitle
postContent {
childMarkdownRemark {
html
}
}
postImage {
title
fluid {
src
}
}
}
}
`
function blogPost({ data }) {
return (
<div>
<img
src={data.post.postImage.fluid.src}
alt={data.post.postImage.title}
></img>
<h1>{data.post.postTitle}</h1>
<h3
dangerouslySetInnerHTML={{
__html: data.post.postContent.childMarkdownRemark.html,
}}
/>
</div>
)
}
export default blogPost
Now i try to create a component which will hold all the blog posts so i can display it on my index.js page, like this:
import { Link, graphql, StaticQuery } from "gatsby"
import React from "react"
import styled from "styled-components"
function BlogSection() {
return (
<StaticQuery
query={graphql`
query blogQuery {
allContentfulPost {
edges {
node {
slug
postTitle
postImage {
file {
url
fileName
}
}
postContent {
postContent
}
postDate
}
}
}
}
`}
render={data => (
<ul>
<Link to={data.allContentfulPost.edges.node.slug}> //here's where the error happens
{data.allContentfulPost.edges.node.postTitle}
</Link>
</ul>
)}
/>
)
}
export default BlogSection
But i get an error Cannot read property 'slug' of undefined which is driving me crazy for days.
any help would be appreciated!
Use:
<ul>
{data.allContentfulPost.edges.map(({ node }) => {
return <Link to={node.slug} key={node.slug}>
{node.postTitle}
</Link>
})}
</ul>
You are querying all pots from Contentful (allContentfulPost) which following the nested structure, has an edges and a node inside: this last one has all the information of your posts (because of the nested structure, you have the slug, the postTitle, etc) so the node, is indeed your post. That said, you only need to loop through edges, which is an array of your posts. In the previous snippet:
data.allContentfulPost.edges.map(({ node })
You are destructuring the iterable variable at the same time you loop through it ({ node }). You can alias it for a more succint approach like:
<ul>
{data.allContentfulPost.edges.map(({ node: post }) => {
return <Link to={post.slug} key={post.slug}>
{post.postTitle}
</Link>
})}
</ul>
It's important to use the key attribute in all loops since it will help React to know what elements are changing.

How to query full size image using gatsby-image?

I'd need a bit of advice with gatsby-image. I'm building a gallery with a custom lightbox.
Query:
export const getAllPhotos = graphql`
query GetAllPhotos {
allStrapiPortfolio {
photos: nodes {
categories {
name
}
photo {
childImageSharp {
fluid(quality: 100) {
...GatsbyImageSharpFluid
}
}
}
strapiId
}
}
}
`;
I'm displaying my images in a grid. photos prop contains images from the query above.
Gallery.js
const Gallery = ({ photos }) => {
const [currentPhotoId, setCurrentPhotoId] = useState(null);
const handleClick = (e) => {
const lightbox = document.getElementById("lightbox");
const div = parseInt(e.target.parentElement.parentElement.className.split(" ")[0]);
const imgSelected = e.target.parentElement.parentElement.className.includes("gatsby-image-wrapper");
if (imgSelected) {
setCurrentPhotoId(div);
lightbox.classList.add("lightbox-active");
} else {
setCurrentPhotoId(null);
lightbox.classList.remove("lightbox-active");
}
};
return (
<main className="portfolio-gallery" onClick={(e) => handleClick(e)}>
photos.map((item) => {
return (
<Image
key={item.strapiId}
fluid={item.photo.childImageSharp.fluid}
data-categories={item.categories[0]}
alt={item.categories[0].name}
className={`${item.strapiId}`}
/>
);
})}
<Lightbox photos={photos} currentPhotoId={currentPhotoId} />
</main>
);
};
export default Gallery;
And after an image is clicked I display my lightbox component
lightbox.js
const Lightbox = ({ photos, currentPhotoId }) => {
const currentPhoto = photos.filter((photo) => photo.strapiId === currentPhotoId)[0];
return currentPhotoId === null ? (
<div id="lightbox" className="lightbox">
<h4>Nothing to display, this is hidden currently</h4>
</div>
) : (
<div id="lightbox" className="lightbox">
<Image fluid={currentPhoto.photo.childImageSharp.fluid} />
</div>
);
};
export default Lightbox;
Problem is that when I display my image in the lightbox stretched across the screen it is in a bad quality as the query downloads images in small sizes. However, my original images are 5000px wide. Unfortunately, as gatsby page queries are generated at the build I'm stuck with this quality.
Any idea about a workaround?
The idea when dealing with multiple images with gatsby-images for your kind of use-case is to query different image resolutions using the art director workaround (breakpoints). The idea, based on the documentation is to:
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"
export default ({ data }) => {
// Set up the array of image data and `media` keys.
// You can have as many entries as you'd like.
const sources = [
data.mobileImage.childImageSharp.fluid,
{
...data.desktopImage.childImageSharp.fluid,
media: `(min-width: 768px)`,
},
]
return (
<div>
<h1>Hello art-directed gatsby-image</h1>
<Img fluid={sources} />
</div>
)
}
export const query = graphql`
query {
mobileImage: file(relativePath: { eq: "blog/avatars/kyle-mathews.jpeg" }) {
childImageSharp {
fluid(maxWidth: 1000, quality: 100) {
...GatsbyImageSharpFluid
}
}
}
desktopImage: file(
relativePath: { eq: "blog/avatars/kyle-mathews-desktop.jpeg" }
) {
childImageSharp {
fluid(maxWidth: 2000, quality: 100) {
...GatsbyImageSharpFluid
}
}
}
}
`

Error Field "frontmatter" is not defined by type MarkdownRemarkFilterInput on Gatsby Static Query

In my Index.tsx page am trying to perform a GraphQL query but I get this error on the browser.
Field "frontmatter" is not defined by type MarkdownRemarkFilterInput.
I am also getting this error on the browser's console
Here is the code of the Index.tsx page
import React from 'react';
import {Link, graphql} from 'gatsby';
// import Intro from '../components/Intro';
import Head from '../components/Head';
import Layout from '../components/Layout';
import Bio from '../components/bio';
interface IndexProps {
readonly data: PageQueryData;
}
const Index: React.FC<IndexProps> = ({data}) => {
const siteTitle = data.site.siteMetadata.title;
const posts = data.allMarkdownRemark.edges;
return (
<Layout title={siteTitle}>
<Head
title="Home"
keywords={[
`blog`,
`gatsby`,
`typescript`,
`javascript`,
`portfolio`,
`react`
]}
/>
<Bio />
<article>
<div className={`page-content`}>
{posts.map(({node}) => {
const title = node.frontmatter.title || node.fields.slug;
return (
<div key={node.fields.slug}>
<h3>
<Link to={node.fields.slug}>{title}</Link>
</h3>
<small>{node.frontmatter.date}</small>
<p dangerouslySetInnerHTML={{__html: node.excerpt}} />
</div>
);
})}
</div>
</article>
</Layout>
);
};
interface PageQueryData {
site: {
siteMetadata: {
title: string;
};
};
allMarkdownRemark: {
edges: {
node: {
excerpt: string;
fields: {
slug: string;
};
frontmatter: {
date: string;
title: string;
};
};
}[];
};
}
export const pageQuery = graphql`
query {
site {
siteMetadata {
title
}
}
allMarkdownRemark(
filter: {frontmatter: {published: {ne: false}}}
sort: {fields: [frontmatter___date], order: DESC}
) {
edges {
node {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
}
}
}
}
}
`;
export default Index;
I do not know if I am performing the query wrong on the allMarkdownRemark part or if I am maybe accessing the data the wrong way. Could someone give me a hint on what is probably going wrong?
Thank you!
The problem was that I still had not added any posts. Once I created one the query worked and the problem disappeared.

Gatsby GraphQL error: Variable "$slug" is never used in operation "BlogPostQuery"

I am unable to pull in the data of my Ghost blog using Gatsby. I am using Ghost as my back end and I am using a package to get the Ghost blog as a source. The problem is just getting the individual posts on the page. Here is blog-post.js:
import React from "react";
export default ({ data }) => {
// const post = data.allGhostPost.edges;
return (
<div>
{/* <h1>{post.title}</h1> */}
{/* <div dangerouslySetInnerHTML={{ __html: post.html }} /> */}
</div>
);
};
export const query = graphql`
query BlogPostQuery($slug: String!) {
allGhostPost {
edges {
node {
id
slug
title
html
published_at
}
}
}
}
`;
Here is my gatsby node file:
exports.createPages = ({ graphql, boundActionCreators}) => {
const {createPage} = boundActionCreators
return new Promise((resolve, reject) => {
const blogPostTemplate = path.resolve(`src/templates/blog-post.js`)
resolve(
graphql(
`
{
allGhostPost(sort: { order: DESC, fields: [published_at] }) {
edges {
node {
id
slug
title
html
published_at
}
}
}
}
`
)
.then(result => {
result.data.allGhostPost.edges.forEach(edge => {
createPage({
path: edge.node.slug,
component: blogPostTemplate,
context: {
slug: edge.node.slug
}
})
})
return;
})
)
})
}
I figured out my problem and it was a problem with my Queries. For anyone working with the Ghost API. This is the answer you will need:
query BlogPostQuery($slug: String!) {
allGhostPost(filter: {slug: {eq: $slug}}) {
edges {
node {
id
slug
title
html
published_at
}
}
}
}
Let me explain my answer.
The issue was that my GraphQL query was not working because the $slug field was not being used within the query. It was just being passed in. That being said, I had to learn a bit of GraphQL to get to my final conclusion.
Using the GraphiQL I was able to find that the allGhostPost had a filter method. Using that I was able to pull in the right result.

Resources