Get data from other custom types in a slice

Hi everyone,

I really hope my question is not too simple but I am new to Nextjs and Prismic.
I search a lot for a solution but I really don't understand why I cannot get it to work.

I started my project with the Prismic NextJs starter "Information Site" and want to add a blog.
I created a Custom type for blog posts and a Custom type for authors.
In the blogposts Custom type I added a Content Relationship element which links to the author Custom type.
In the default page Custom type I added a Content Relationship element which links to the blogposts Custom type.

I added a [uid].js in pages/authors/ which generates a page for every author I created.
I added a [uid].js in pages/blog/ which generates a page for every blog posts I created. I have my page but I cannot get the author data.
I only get

{
    "id": "Y2Ix7xQAACQAlbmL",
    "type": "author",
    "tags": [],
    "lang": "en-us",
    "slug": "eric",
    "first_publication_date": "2022-11-02T09:01:38+0000",
    "last_publication_date": "2022-11-02T09:01:38+0000",
    "uid": "eric",
    "link_type": "Document",
    "isBroken": false
}

I don't get the name, position and picture data from the author Custom Type.

In the various pages of my website I want to have a slice which displays 3 blog posts selected by the content manager.
I created a slice to display the posts and in the Repeatable Zone added a Content Relationship element so the content manager can select the desired posts.

I canot get the blog post data nor the author data.

{
    "variation": "default",
    "version": "sktwi1xtmkfgx8626",
    "items": [
        {
            "blogpost": {
                "id": "Y2IyexQAALOqlbuw",
                "type": "blogposts",
                "tags": [],
                "lang": "en-us",
                "slug": "lorem-ipsum",
                "first_publication_date": "2022-11-02T09:04:00+0000",
                "last_publication_date": "2022-11-02T09:04:00+0000",
                "uid": "lorem-ipsum",
                "link_type": "Document",
                "isBroken": false
            }
        },
        {
            "blogpost": {
                "id": "Y2Iy0RQAACUAlb1w",
                "type": "blogposts",
                "tags": [],
                "lang": "en-us",
                "slug": "lorem-ipsum-2",
                "first_publication_date": "2022-11-02T09:05:25+0000",
                "last_publication_date": "2022-11-02T09:05:25+0000",
                "uid": "lorem-ipsum-2",
                "link_type": "Document",
                "isBroken": false
            }
        },
        {
            "blogpost": {
                "id": "Y2Iy-RQAALOqlb41",
                "type": "blogposts",
                "tags": [],
                "lang": "en-us",
                "slug": "lorem-ipsum-3",
                "first_publication_date": "2022-11-02T09:06:04+0000",
                "last_publication_date": "2022-11-02T09:06:04+0000",
                "uid": "lorem-ipsum-3",
                "link_type": "Document",
                "isBroken": false
            }
        }
    ],
    "primary": {
        "title": [
            {
                "type": "heading1",
                "text": "Blog posts",
                "spans": []
            }
        ],
        "description": [
            {
                "type": "paragraph",
                "text": "List of blog posts",
                "spans": []
            }
        ]
    },
    "id": "blog_posts$acc0df38-b9db-4130-9773-c3189d476958",
    "slice_type": "blog_posts",
    "slice_label": null
}

I created a public github repo with an example of my problem if you want to see:

And the vercel published website:
https://test-blogposts.vercel.app/

I hope my problem is well described.

Thank you for your help.

Hi Arnaud,

Welcome to the community!

Your description was very clear thank you.

Option 1:
To could get linked you'll need to use query options to specify the data you want:

Since your data is in a Slice, you'll need the GraphQuery option:

Option 2:
You could also do 2 queries, 1 for the author type, and then for every post made by the author using predicates:

With a filter by the content relationship of the author:

Let me know if you have any questions.

Hi Phil,

Thank you for your rapid answer.
I have to say I had already seen the docs but I don't understand how to use them.
When I try to use getStaticProps I don't get props in my slice.
I tested it in my BlogPosts slice and got undefined:

import React from 'react'
import { PrismicRichText, PrismicLink } from '@prismicio/react'
import { createClient } from '../../prismicio'

const BlogPosts = ({ slice, document }) => {
  console.log(slice)
  console.log(document)
  return (
    <section className='container px-5 mx-auto py-24'>
      <div className='text-center flex flex-col space-y-4'>
        <span className="">
          <PrismicRichText field={slice.primary.title}/>
        </span>
        <span className=''>
          <PrismicRichText field={slice.primary.description}/>
        </span>
      </div>
      <ul className="grid grid-cols-1 gap-8 md:grid-cols-3 mt-8">
          {slice.items.map((item) => (
            <li key={item.blogpost.id} className="bg-gray-100" >
              <div className='bg-gray-200 h-24 flex flex-col justify-center items-center text-center'>
                <span className="">Picture</span>
              </div>
              <div className='p-5 flex flex-col space-y-2'>
                <span>Title : xxxxxxxxxxx</span>
                <span>Content : xxxxxxxxxxx</span>
                <span>Author : xxxxxxxxxxx</span>
                <div className='mt-2'>
                  Link to the post : <PrismicLink document={item.blogpost}>Link</PrismicLink>
                </div>
              </div>
            </li>
          ))}
      </ul>
      
    </section>
  )
}

export default BlogPosts

export async function getStaticProps({ params, locale, previewData }) {
  const client = createClient({ previewData });
  client.getByUID("blogposts", params.uid, {
    graphQuery: `
    {
      author {
        name,
        position,
        picture
      }
    }
  `
  ,lang: locale });

  return {
    props: { document },
  };
}

export async function getStaticPaths() {
  const client = createClient();
  const posts = await client.getAllByType("blogposts", { lang: "*" });
  return {
    paths: posts.map((post) => {
      return {
        params: { uid: post.uid },
        locale: post.lang,
      };
    }),
    fallback: false,
  };
}

Maybe I misunderstood the docs?
https://test-blogposts.vercel.app/

Yes, with GraphQuery, you need to specify ALL the data on your Custom Type, not just the linked content.

Thank you,
I managed to get it to work for the blog post page.
With GraphQuery I can get authors data like this :
const document = await client.getByUID("blogposts", params.uid, { graphQuery: { blogposts { uid title picture contenttext author { ...on author { uid picture name position } } } } })`

But now I still cannot get my slice to work.
In the various pages of my website I want to have a slice which displays 3 blog posts selected by the content manager.
I created a slice to display the posts and in the Repeatable Zone added a Content Relationship element so the content manager can select the desired posts to display.

But when I use getStaticProps in the slice I don't get any props other than the slice's ones.
I only get "undefined". (you can see it in the browser console)

How can I get more props in the slice?
https://test-blogposts.vercel.app/

Thank again for your help!

When it comes to a Slice, you don't need to use getStaticProps in the Slice, this data is passed down from the document.

From your example, it looks like your Slice isn't in the Slice Zone.

  1. Did you create the component using Slice Machine?
  2. Can you show me your component code?
  3. Can you show me where you import your component?

Thanks.

Ok thank for the response.
My answers to your questions:

  1. Yes I used Slice Machine to create the component
  2. My component code :
import React from 'react'
import { PrismicRichText, PrismicLink } from '@prismicio/react'

const BlogPosts = ({ slice }) => {
  return (
    <section className='container px-5 mx-auto py-24'>
      <div className='text-center flex flex-col space-y-4'>
        <span className="">
          <PrismicRichText field={slice.primary.title}/>
        </span>
        <span className=''>
          <PrismicRichText field={slice.primary.description}/>
        </span>
      </div>
      <ul className="grid grid-cols-1 gap-8 md:grid-cols-3 mt-8">
          {slice.items.map((item) => (
            <li key={item.blogpost.id} className="bg-gray-100" >
              <div className='bg-gray-200 h-24 flex flex-col justify-center items-center text-center'>
                <span className="">Picture</span>
              </div>
              <div className='p-5 flex flex-col space-y-2'>
                <span>Title : xxxxxxxxxxx</span>
                <span>Content : xxxxxxxxxxx</span>
                <span>Author : xxxxxxxxxxx</span>
                <div className='mt-2'>
                  Link to the post : <PrismicLink document={item.blogpost}>Link</PrismicLink>
                </div>
              </div>
            </li>
          ))}
      </ul>
      
    </section>
  )
}

export default BlogPosts
  1. I didn't touch anything else from the Prismic NextJs starter "Information Site".
    As I need the slice to show on the "Page" Custom Type I think you want to to see the [uid].js code which is in the Pages folder:
import Head from "next/head";
import { SliceZone } from "@prismicio/react";
import * as prismicH from "@prismicio/helpers";

import { createClient } from "../prismicio";
import { components } from "../slices";
import { Layout } from "../components/Layout";

const Page = ({ page, navigation, settings }) => {
  return (
    <Layout navigation={navigation} settings={settings}>
      <Head>
        <title>
          {prismicH.asText(page.data.title)} |{" "}
          {prismicH.asText(settings.data.siteTitle)}
        </title>
      </Head>
      <SliceZone slices={page.data.slices} components={components} />
    </Layout>
  );
};

export default Page;

export async function getStaticProps({ params, locale, previewData }) {
  const client = createClient({ previewData });

  const page = await client.getByUID("page", params.uid, { lang: locale });
  const navigation = await client.getSingle("navigation", { lang: locale });
  const settings = await client.getSingle("settings", { lang: locale });

  return {
    props: {
      page,
      navigation,
      settings,
    },
  };
}

export async function getStaticPaths() {
  const client = createClient();

  const pages = await client.getAllByType("page", { lang: "*" });

  return {
    paths: pages.map((page) => {
      return {
        params: { uid: page.uid },
        locale: page.lang,
      };
    }),
    fallback: false,
  };
}

If you want to see more her's the github:

Thanks again for your help :slight_smile:

OK, so there are a few different things.

  1. So you can't fetch data in Next.js with getStaticProps on the component level.
  2. You need to use GraphQuery on your home page query so that you can get all the data there and pass the data down as props to your Slice:
const page = await client.getByUID("page", "home", { lang: locale, graphQuery: ` ... ` });

Although you will need to specify all the content and Slices in your page Custom Type in the GraphQuery.

Does make sense?

Ok thank you very much Phil, it works !
The only dowside is that if anyone adds a slice to the Custom Type we'll have to remember to update the GraphQuery, and it's already huge!

Thanks again.

Arnaud

Yes, it's true GraphQuery can be quite a task to maintain. It's something the team is aware of and would like to improve in the future, though it's not in our roadmap right now.

Here's a workaround I offered another user where you can do 2 queries and merge the data so you don't need to keep updating the GraphQuery (It's tricky, but might save you time in the long run):

1 Like

Yes I'm a going to try this, thanks !

1 Like

The problem is that you are trying to put Orderline pointers into a slice that wants Orderline values.

type Order struct {
    Id int64
    Customer *Customer
    Orderlines *[]Orderline
}

Change this field's type from

Orderlines *[]Orderline

to...

Orderlines []*Orderline

You also need to change...

ols := []Orderline{&ol1, &ol2}

to

ols := []*Orderline{&ol1, &ol2}

In most cases defining a *slicetype is redundant because slices, maps, and channels are already reference types. In otherwords if you pass the value of a slice defined in main into a function, changes made to the copied slice's indices will mutate the original slice defined in main as well.

However, it's important to note that slices become decoupled from each other when an individual copy's underlying array is forced to grow it's capacity as a result of appending data to your slice. Therefore in certain scenarios you may find a pointer to a slice ideal or even necessary.

For More : https://knifeplatoon.com/