Отображение содержимого каталога (файлов уценки) в Gatsby с использованием graphql

Я использую стек Gatsby/Netlify CMS и пытаюсь отобразить содержимое файла уценки на главной странице. Например, у меня есть каталог в src/pages/experience, в котором отображаются все файлы уценки опыта.

Итак, используя graphql, у меня есть запрос, который действительно работает:

{
        allMarkdownRemark(
         limit: 3,
         sort: { order: DESC, fields: [frontmatter___date] },
         filter: { fileAbsolutePath: { regex: "/(experience)/" } }
       ) {
           edges {
             node {
               id
               frontmatter {
                 title
                 company_role
                 location
                 work_from
                 work_to
                 tags
               }
               excerpt
             }
           }
         }
     }

Однако при запуске на странице моего компонента отображается × TypeError: невозможно прочитать свойство allMarkdownRemark неопределенного

Однако после ввода этого перед возвратом:

if (!data) { return null };

Ошибка исчезает, но весь раздел исчезает. Вот оно ниже:

const Experience = ({data}) => {

return (

    <div id = "experience" className = "section accent">
              <div className = "w-container">
                  <div className = "section-title-group">
                    <Link to = "#experience"><h2 className = "section-heading centered">Experience</h2></Link>
                  </div>
              <div className = "columns w-row">
                     {data.allMarkdownRemark.edges.map(({node}) => (
                        <div className = "column-2 w-col w-col-4 w-col-stack" key = {node.id}>
                            <div className = "text-block"><strong>{node.frontmatter.title}</strong></div>
                            <div className = "text-block-4">{node.frontmatter.company_role}</div>
                            <div className = "text-block-4">{node.frontmatter.location}</div>
                            <div className = "text-block-3">{node.frontmatter.work_from} – {node.frontmatter.work_to}</div>
                            <p className = "paragraph">{node.frontmatter.excerpt}</p>
                            <div className = "skill-div">{node.frontmatter.tags}</div>
                        </div>
                     ))} 
              </div>
          </div>
      </div>
)}

export default Experience

В gatsby-config-js я добавил разрешение gatsby-source-filesystem отдельно от /src/posts в /src/pages, где каталог опыта — src/pages/experience.

Обновление: 07.02.2019 Вот файл gatsby-config-js:

module.exports = {
  siteMetadata: {
    title: `Howard Tibbs Portfolio`,
    description: `This is a barebones template for my portfolio site`,
    author: `Howard Tibbs III`,
    createdAt: 2019
  },
    plugins: [
      `gatsby-plugin-react-helmet`,
      {
        resolve: `gatsby-source-filesystem`,
        options: {
          name: `images`,
          path: `${__dirname}/src/images`,
          },
      },
      {
        resolve: 'gatsby-transformer-remark',
        options: {
          plugins: [
            {
              resolve: 'gatsby-remark-images',
            },
          ],
        },
      },
      {
        resolve: `gatsby-source-filesystem`,
        options: {
          name: `posts`,
          path: `${__dirname}/src/posts`,
          },
      },
      {
        resolve: `gatsby-source-filesystem`,
        options: {
          name: `pages`,
          path: `${__dirname}/src/pages`,
          },
      },
        `gatsby-plugin-netlify-cms`,
        `gatsby-plugin-sharp`,
        {
          resolve: `gatsby-plugin-manifest`,
          options: {
            name: `gatsby-starter-default`,
            short_name: `starter`,
            start_url: `/`,
            background_color: `#663399`,
            theme_color: `#663399`,
            display: `minimal-ui`,
            icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
          },
        },
        `gatsby-transformer-sharp`
    ],
  }

Я чувствую, что где-то в gatsby-node-js я не создал экземпляр, чтобы что-то делать с запросом этого типа.

const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')

const PostTemplate = path.resolve('./src/templates/post-template.js')
const BlogTemplate = path.resolve('./src/templates/blog-template.js')

exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions
    if (node.internal.type === 'MarkdownRemark') {
        const slug = createFilePath({ node, getNode, basePath: 'posts' })
    createNodeField({
        node,
        name: 'slug',
        value: slug,
    })

}
}

exports.createPages = async ({ graphql, actions }) => {

const { createPage } = actions
const result = await graphql(`
    {
        allMarkdownRemark (limit: 1000) {
          edges {
            node {
              fields {
                slug
              }

            }
          }
        }
      }
`)

const posts = result.data.allMarkdownRemark.edges

posts.forEach(({ node: post }) => {
    createPage({
        path: `posts${post.fields.slug}`,
        component: PostTemplate,
        context: {
            slug: post.fields.slug,
        },
    })
})

const postsPerPage = 2
const totalPages = Math.ceil(posts.length / postsPerPage)

Array.from({ length: totalPages }).forEach((_, index) => {
    const currentPage = index + 1
    const isFirstPage = index === 0
    const isLastPage = currentPage === totalPages

    createPage({
        path: isFirstPage ? '/blog' : `/blog/${currentPage}`,
        component: BlogTemplate,
        context: {
            limit: postsPerPage,
            skip: index * postsPerPage,
            isFirstPage,
            isLastPage,
            currentPage,
            totalPages,
        },
    })
})
}

Хотел узнать, смог ли кто-нибудь получить что-то похожее на работу? Очень ценю вашу помощь.


Обновление: 06.02.2019

Итак, я внес некоторые изменения в свой код с pageQuery на StaticQuery, и, к сожалению, он все еще не работает, но я считаю, что он движется в правильном направлении:

export default() => (

    <div id = "experience" className = "section accent">
              <div className = "w-container">
                  <div className = "section-title-group">
                    <Link to = "#experience"><h2 className = "section-heading centered">Experience</h2></Link>
                  </div>
              <div className = "columns w-row">
              <StaticQuery
              query = {graphql`
                  query ExperienceQuery {
                      allMarkdownRemark(
                       limit: 2,
                       sort: { order: DESC, fields: [frontmatter___date]},
                       filter: {fileAbsolutePath: {regex: "/(experience)/"}}
                     ) {
                         edges {
                           node {
                             id
                             frontmatter {
                               title
                               company_role
                               location
                               work_from
                               work_to
                               tags
                             }
                             excerpt
                           }
                         }
                       }
                   }

              `}
              render = {data => (
                  <div className = "column-2 w-col w-col-4 w-col-stack" key = {data.allMarkdownRemark.id}>
                  <div className = "text-block"><strong>{data.allMarkdownRemark.frontmatter.title}</strong></div>
                  <div className = "text-block-4">{data.allMarkdownRemark.frontmatter.company_role}</div>
                  <div className = "text-block-4">{data.allMarkdownRemark.frontmatter.location}</div>
                  <div className = "text-block-3">{data.allMarkdownRemark.frontmatter.work_from} – {data.allMarkdownRemark.frontmatter.work_to}</div>
                  <p className = "paragraph">{data.allMarkdownRemark.frontmatter.excerpt}</p>
                  <div className = "skill-div">{data.allMarkdownRemark.frontmatter.tags}</div>
                  </div>
              )}
              />
              </div>
          </div>
      </div>
);

Я получаю эту ошибку TypeError: Не удается прочитать заголовок свойства undefined

Итак, что я пытаюсь сделать, это этот экземпляр в этом разделе. Конечно, это заполнитель, но я хочу заменить этот заполнитель содержимым каждой уценки. Отрывок опыта


Обновление: 07.02.2019

Итак, сегодня никаких изменений, но я хотел опубликовать несколько полей, чтобы лучше понять, что я пытаюсь сделать. Это файл config.yml из NetlifyCMS, в котором отображаются коллекции. Это то, что я выполняю (Примечание: тестовое репо предназначено только для того, чтобы увидеть фактическую CMS, я постараюсь изменить):

backend:
  name: test-repo
  branch: master

media_folder: static/images
public_folder: /images

display_url: https://gatsby-netlify-cms-example.netlify.com/

# This line should *not* be indented
publish_mode: editorial_workflow

collections:

  - name: "experience"
    label: "Experience"
    folder: "experience"
    create: true
    fields:
        - { name: "title", label: "Company Title", widget: "string" }
        - { name: "company_role", label: "Position Title", widget: "string" }
        - { name: "location", label: "Location", widget: "string" }
        - { name: "work_from", label: "From", widget: "date", format: "MMM YYYY" }
        - { name: "work_to", label: "To", default: "Present", widget: "date", format: "MMM YYYY" }
        - { name: "description", label: "Description", widget: "text" }
        - { name: "tags", label: "Skills Tags", widget: "select", multiple: "true", 
              options: ["ReactJS", "NodeJS", "HTML", "CSS", "Sass", "PHP", "Typescript", "Joomla", "CMS Made Simple"] }


  - name: "blog"
    label: "Blog"
    folder: "blog"
    create: true
    slug: "{{year}}-{{month}}-{{day}}_{{slug}}"
    fields:
      - { name: path, label: Path }
      - { label: "Image", name: "image", widget: "image" }
      - { name: title, label: Title }
      - { label: "Publish Date", name: "date", widget: "datetime" }
      - {label: "Category", name: "category", widget: "string"}
      - { name: "body", label: "body", widget: markdown }
      - { name: tags, label: Tags, widget: list }


  - name: "projects"
    label: "Projects"
    folder: "projects"
    create: true
    fields:
      - { name: date, label: Date, widget: date }
      - {label: "Category", name: "category", widget: "string"}
      - { name: title, label: Title }
      - { label: "Image", name: "image", widget: "image" }
      - { label: "Description", name: "description", widget: "text" }
      - { name: body, label: "Details", widget: markdown }
      - { name: tags, label: Tags, widget: list}


  - name: "about"
    label: "About"
    folder: "src/pages/about"
    create: false
    slug: "{{slug}}"
    fields:
      - {
          label: "Content Type",
          name: "contentType",
          widget: "hidden",
          default: "about",
        }
      - { label: "Path", name: "path", widget: "hidden", default: "/about" }
      - { label: "Title", name: "title", widget: "string" }
      - { label: "Body", name: "body", widget: "markdown" }

И для примера страницы уценки это будет формат, который нужно искать в разделе «Опыт», потому что, как вы видите на картинке, он отображается в контейнере:

---
title: Test Company
company_role: Test Role
location: Anytown, USA
work_from: January, 2020
work_to: January, 2020
tags: Test, Customer Service
---

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.

Обновление: 08.02.2019

У меня есть некоторые обновления с кодом, представленным ниже, но прежде чем я приступлю к этому, вот несколько изображений того, что я хочу сделать. Это заполнители, которые я хочу заменить реальными данными. Это для каждого раздела:

Фрагмент полного опыта

Фрагмент проектов

Фрагмент блога

Я запустил код, предоставленный @staypuftman в ответе ниже, и получил эту ошибку:

Your site's "gatsby-node.js" created a page with a component that doesn't exist.

Я добавил код в дополнение к тому, что уже было, и он обработал эту ошибку. Это то, что я изначально предполагал, и причина, по которой я хотел использовать StaticQuery независимо. На самом деле это была основная проблема, с которой я столкнулся при работе с документацией и начальными репозиториями: никто не создавал несколько переменных в node.js.

Я также попробовал версию @DerekNguyen, которая выглядела так:

import React from "react"
import { Link, graphql, StaticQuery } from "gatsby"



export default(data) => (

        <div id = "experience" className = "section accent">
                  <div className = "w-container">
                      <div className = "section-title-group">
                        <Link to = "#experience"><h2 className = "section-heading centered">Experience</h2></Link>
                      </div>
                  <div className = "columns w-row">
                  <StaticQuery
                  query = {graphql`
                      query ExperienceQuery {
                          allMarkdownRemark(
                           limit: 2,
                           sort: { order: DESC, fields: [frontmatter___date]},
                           filter: {fileAbsolutePath: {regex: "/(experience)/"}}
                         ) {
                             edges {
                               node {
                                 id
                                 frontmatter {
                                   title
                                   company_role
                                   location
                                   work_from
                                   work_to
                                   tags
                                 }
                                 excerpt
                               }
                             }
                           }
                       }

                  `}
                  render = {data.allMarkdownRemark.edges.map(({ node }) => (
                      <div className = "column-2 w-col w-col-4 w-col-stack" key = {node.id}>
                      <div className = "text-block"><strong>{node.frontmatter.title}</strong></div>
                      <div className = "text-block-4">{node.frontmatter.company_role}</div>
                      <div className = "text-block-4">{node.frontmatter.location}</div>
                      <div className = "text-block-3">{node.frontmatter.work_from} – {node.frontmatter.work_to}</div>
                      <p className = "paragraph">{node.frontmatter.excerpt}</p>
                      <div className = "skill-div">{node.frontmatter.tags}</div>
                      </div>
                  ))}
                  />
                  </div>
              </div>
          </div>
);

Однако это также привело к ошибке:

TypeError: Cannot read property 'edges' of undefined

Все еще работаю над этим, но я думаю, что он приближается к решению. Имейте в виду, что мне также пришлось бы создать его для других переменных.


Обновление: 10.02.2019

Для тех, кто хочет увидеть, как я создал сайт с помощью gatsby-starter, вот он ниже:

Мое портфолио

Где этот компонент Experience находится в вашем src? Может быть, вы используете страничный запрос в нестраничном компоненте?

Derek Nguyen 06.02.2019 05:10

Таким образом, компонент experience.js находится в другом каталоге. Это в src/components/Experience. У меня каждый раздел главной страницы разделен на подкомпоненты, и они идут в соответствующие каталоги. И затем, если мне нужно добавить определенную страницу в этот раздел, она переходит в этот каталог.

H.Tibbs 06.02.2019 07:19

Тогда вы не сможете экспортировать запрос graphql и ожидать data в реквизитах, gatsby проигнорирует ваш запрос! Вместо этого вы должны использовать Статикзапрос

Derek Nguyen 06.02.2019 07:21

У меня StaticQuery закомментирован мой фактический код. Если это сработает, большое спасибо @DerekNguyen

H.Tibbs 06.02.2019 10:26

Это не сработало для меня, я должен что-то упустить. Я буду поддерживать StaticQuery, но чего-то не хватает.

H.Tibbs 06.02.2019 11:49

Не могли бы вы отредактировать ответ, чтобы добавить часть статического запроса?

Derek Nguyen 06.02.2019 11:57

Извините за это, был далеко от моего компьютера

H.Tibbs 06.02.2019 21:20

Странно использовать Staticquery и gatsby-node.js вместе вот так. Если вы извлекаете данные с помощью gatsby-node.js, вы должны передать это компоненту и не нуждаться в дополнительных данных через Staticquery. Возможно, есть проблема с архитектурой, которую может решить улучшенная композиция компонентов.

serraosays 07.02.2019 15:40

Вы также должны опубликовать свою модель данных уценки, потому что ошибка, которую вы получаете, в основном означает, что Webpack говорит, что не может найти title в вашем файле уценки.

serraosays 07.02.2019 15:41

@staypuftman Мне еще предстоит подключить сторону NetlifyCMS, но я могу показать вам, что я ищу в коллекциях в config.yml. Я не уверен, где находится модель данных уценки, на которую вы ссылаетесь, но если вам нужен config.yml от Netlify, я мог бы опубликовать это. Из-за уценки, которая уже есть в моем каталоге, у каждого есть заголовок. Я опубликую один.

H.Tibbs 07.02.2019 22:10

Я думаю, что в вашем запросе ошибка: fields: [frontmatter___date] без скобки на конце. Вы закрываете allMarkdownRemark, хотя этого делать не следует.

serraosays 07.02.2019 22:26

Я также добавил файл config.js. Я полагаю, что не понимаю, как получить данные из gatsby-node.js, поскольку я чувствовал, что как только вы создадите запрос из graphql, он сможет отобразить эту информацию. По сути, я не уверен, где бы я определил это в node.js.

H.Tibbs 07.02.2019 22:31

@staypuftman Запрос, который у меня есть, находится в самом верху, и я быстро внесу изменения здесь. Сделайте пробел между скобками в полях: [frontmatter___date].

H.Tibbs 07.02.2019 22:40

В вашем методе рендеринга StaticQuery вы обрабатываете allMarkdownRemark, как если бы это был узел. Это не так, вместо этого вам нужно будет сопоставить allMarkdownRemark.egdes, чтобы получить узел, то есть data.allMarkdownRemark.edges.map(({ node }) => (<div>...</div>)).

Derek Nguyen 08.02.2019 05:40

Я вижу, как я сделал это в начальном блоке, только вместо StaticQuery. Я проверю это.

H.Tibbs 08.02.2019 07:25
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
15
1 879
1

Ответы 1

gastby-node.js используется, когда у вас есть несколько страниц, которые должны находиться на /pages/{variable-here}/. Gatsby использует gatsby-node.js для выполнения запроса GraphQL к вашему источнику данных (в данном случае — Netlify CMS) и получает весь необходимый контент на основе вашего конкретного запроса GraphQL.

Затем он динамически создает X страниц, используя компонент вашего проекта. Сколько страниц он создает, зависит от того, что он находит в удаленном источнике данных. То, как они выглядят, зависит от указанного вами компонента. Подробнее об этом читайте в статье Гэтсби учебник.

Staticquery используется для получения одноразовых данных в компоненты, а не для генерации страниц из источника данных. Это очень полезно, но не то, что, я думаю, вы пытаетесь сделать. Подробнее об этом читайте на Сайт Гэтсби.

На основании всего этого и того, что вы предоставили выше, я думаю, что ваш gatsby-node.js должен выглядеть так:

// Give Node access to path
const path = require('path')

// Leverages node's createPages capabilities
exports.createPages = async ({ graphql, actions }) => {

  // Destructures createPage from redux actions, you'll use this in a minute
  const { createPage } = actions

  // Make your query
  const allExperiencePages = await graphql(`
    { 
      allMarkdownRemark(limit: 1000) {
        edges {
          node {
            id
            frontmatter {
              title
              company_role
              location
              work_from
              work_to
              tags
            }
            excerpt
          }  
        }
      }
    }
  `)

  // gatsby structures allExperiencePages into an object you can loop through
  // The documentation isn't great but the 'data' property contains what you're looking for
  // Run a forEach with a callback parameter that contains each page's data
  allExperiencePages.data.allMarkdownRemark.edges.forEach( page => {

    // Make individual pages with createPage variable you made earlier
    // The 'path' needs to match where you want the pages to be when your site builds
    // 'conponent' is what Gatsby will use to build the page
    // 'context' is the data that the component will receive when you `gatsby build`
    createPage({
      path: `/pages/${page.node.title}/`,
      component: path.resolve('src/components/Experience'),
      context: {
        id: page.node.id,
        title: page.node.frontmatter.title,
        company_role: page.node.frontmatter.company_role,
        location: page.node.frontmatter.location,
        work_from: page.node.frontmatter.work_from,
        work_to: page.node.frontmatter.work_to,
        tags: page.node.frontmatter.tags,
        excerpt: page.node.excerpt
      }
    })
  }) 

}

Одного этого может быть недостаточно для создания страницы! Все зависит от того, что происходит с компонентом, который вы указываете в компонентной части createPage файла gatsby-node.js.

Большое спасибо за этот ответ. Я попробую это со своей стороны. Я считаю, что избегал создания страниц для страниц уценки опыта. Я думал, что вы можете создавать экземпляры нужных вам полей в компоненте, не создавая страницу из этого конкретного файла. Я буду работать над этим и дам вам знать, что я получаю.

H.Tibbs 07.02.2019 23:11

Кроме того, и именно по этой причине я делаю это, также будут страница проектов и страница блога, в каждом разделе которых будут отображаться 3 самых последних экземпляра на главной странице. С тем, что у вас есть выше, могу ли я создать константу для allProjects и allBlogs таким же образом?

H.Tibbs 07.02.2019 23:16

Судя по вопросу ОП, похоже, что он хочет иметь независимый компонент Experience, который затем можно будет разместить внутри других страниц или шаблонов сообщений, а не создавать отдельные страницы опыта. @ Х.Тиббс, верно?

Derek Nguyen 08.02.2019 05:53

@DerekNguyen Правильно. Поэтому мне нужны эти компоненты для заполнения новых экземпляров: опыт, проекты и блоги. Каждый из них будет отображать 3 самых последних экземпляра (например, мой самый последний опыт, самые последние проекты и т. д.) на главной странице, но когда вы нажимаете на текст раздела или «Просмотреть больше» (думая добавить этот призыв к действию), он отобразит весь список этого компонента в порядке убывания. Например, страница блога, на которой перечислены все блоги, а затем щелкает каждый пост в блоге.

H.Tibbs 08.02.2019 07:30

Кроме того, записи об опыте не обязательно должны быть отдельной публикацией, но для простоты это может быть тот же тип уценки.

H.Tibbs 08.02.2019 07:32

Можете ли вы опубликовать визуальные эффекты того, как это будет выглядеть? Какие-либо страницы генерируются динамически?

serraosays 08.02.2019 15:43

@staypuftman Я опубликую несколько фотографий того, что я пытаюсь сделать выше в исходном вопросе. Проекты, Впечатления и Блоги будут генерироваться динамически, потому что каждый из них можно добавлять или удалять. Содержимое каждого раздела будет создаваться и поддерживаться через NetlifyCMS, где вы можете добавлять, редактировать или удалять их.

H.Tibbs 08.02.2019 20:30

Но ключевой вопрос заключается в том, какие СТРАНИЦЫ будут динамически генерироваться. Вы динамически генерируете страницы в gatsby-node.js. Динамически генерируемые РАЗДЕЛЫ происходят с помощью «Статического запроса» на статических страницах.

serraosays 08.02.2019 21:17

Я считаю, что пытался динамически генерировать разделы. Таким образом, переосмыслив это, нужно будет создавать страницы, потому что каждая страница или уценка будут иметь уникальное свойство. Так что каждый опыт будет другим, каждый проект будет другим, каждый блог будет другим. Извините за путаницу.

H.Tibbs 09.02.2019 11:53

Я думаю, вам нужно сочетание обеих динамических страниц с динамически генерируемыми разделами. Таким образом, у вас будут динамически сгенерированные страницы, как я сделал выше, а затем внутри компонента, на который вы ссылаетесь, внутри createPage, куда пойдет статический запрос. Дайте мне знать, если это имеет смысл.

serraosays 10.02.2019 18:29

Я покажу внутреннюю часть песочницы кода, чтобы показать вам, как она устроена. Может быть, это даст лучшее представление о том, к чему я стремлюсь. Но, как я уже упоминал о node.js, я считаю, что именно здесь код должен быть первым, прежде чем он будет передан компонентам, и именно в этом заключается сложность.

H.Tibbs 10.02.2019 22:09

Другие вопросы по теме