We will create a new category model where we can reference all of the products that we want in a two-way reference. That means, not only can we reference those products on our category, like we worked on in the previous lesson, we can go the opposite direction on our products node. We will reference those categories and get all the categories that relate to a particular product.
With that data, we will set up a dynamic route for category slug, where we use both getStaticProps and getStaticPaths to build each and every one of those pages, where we will dynamically generate each category page with all the products.
Colby Fayock: [0:00] Now that we have our categories set up with products added, we want to be able to query this data so that we can use it inside of our app. As usual, let's head over to our Playground Explorer where we can scroll down, and we see we have both category and categories available.
[0:13] Let's first navigate into categories, where we can see all of the data attributes that we have available, where we want to select our ID, our name, our slug, and we see that we do have our products' node.
[0:24] Now, if we start to scroll down through our products' node, we can see the typical items like our ID. We have our name and our slug, but what else do we want to add so that on product listing pages, we show only the data that we need?
[0:38] We can think of this similarly to what we currently have on our home page, where we have our image. We have our name, our price, and we are using our slug because we want to tell Snipcart exactly where that product is.
[0:49] Inside of our home page query, we can see exactly that where we see all the fields, including the ID name, price, slug, and image. Let's make sure we also have our image and our price. If we hit play, we can see that we're going to get a list of all of our categories, including our first one of apparel, where we get all of the products that are inside of that particular apparel category.
[1:09] Now, when building the category routes, we're going to do this pretty much exactly the same like we did with our products where, first, we want to generate the paths. Then, we want to grab the data for each of those paths.
[1:21] To start off, inside of the pages, categories, category.js file. This is where we have a template similar to what we did with the product template, where we can get started building this out.
[1:32] If we look at this route of categories, category, we can see that we have our category name. Then, we're going to have a place where we can list out all the products. Let's add those products in. Like our productslug.js, the first thing we're going to want to do is use getStaticPaths in order to query all the paths.
[1:47] Let's head over to productslug, and let's copy this entire function because it's going to be very similar. Let's head to the very bottom of category.js where we can paste in that function.
[1:59] Now, because we're using Apollo Client, we want to make sure we also copy over that import statement so that we can use that as a dependency. Now, we can update this query to grab the category data. I'm going to grab this entire query and replace my page products query, where I can now call that page categories.
[2:17] Because, at this point, we're only creating the category paths, we don't need all this additional data inside of this particular query. I'm going to go ahead and remove the products. I'm also going to remove the name. We want to keep the ID because that can sometimes help with caching. We also want to query for the slug so that we can create that path.
[2:36] Now, inside of our path creation. This is going to be very similar as well, but instead of products, we want to grab our categories, where for each category, we want to grab the category slug and we're going to create this parameter as category slug.
[2:51] That means, in addition to using this param of category slug inside of our paths, we want to update our file name to make that a dynamic route, where it uses category slug as the parameter.
[3:04] As soon as the page reloads, we can see that we're getting a 404 on category, because we don't have a category called category. If we change that to apparel, we can see that the page is going to load, but now we're getting that getStaticProps error.
[3:17] Over inside a productslug.js, I'm going to be lazy again and grab my getStaticProps function, where I'm going to paste that in right above my getStaticPaths. As we learned the last time that we created dynamic routes, we don't want to simply query for all of the categories when we're trying to build out a single page.
[3:36] Instead, we don't want to query the category. We're going to query a single category where we can use a slug where we're going to create a variable, where we can say that we want the query variables with that slug to equal a particular value, such as apparel.
[3:52] Once I click play, we can see like before, it added that ID, but we can see that I get my category. I'm going to add back in all of my category fields such as name and slug. I'm going to add my products. Remember, we want to add our ID, our image, our name, our price, our slug.
[4:12] Before we head over, let's test out that this works and we can see we have all of our beautiful data. Let's copy this query and I'm going to replace this page product query, where the only thing I'm going to update is my query to page category.
[4:26] Then instead of using our parameter of product slug like we did in the product page, we want to make sure that we're using the category slug. I'm going to change that to category slug. I'm also going to update the product instances to category.
[4:41] As a quick shortcut, I'm going to add a second prop called products, which is going to be equal to category.products. At this point, we should have our data available to destructure as props. I'm going to have my category and my products. Let's console.log out those values, just so as usual we can see that we're getting our data.
[5:02] Once the page refreshes, we can see we get that template again. If we open up our console, we see we both get our category object, but we also get those products which again is a little shortcut so that we don't have to reference it inside of the category object.
[5:17] Now that we have that data, let's populate our page. As we're looking through instead of spelling out category name, we want to say category.name and we can also use that value inside of our title. Then we're going to use our products so that we can map through each and every one of our products.
[5:34] That way, we can return a new list item for every single one of those products. I'm going to return a new instance of that list item where I can start replacing that data such as the product.name, and the product.price. Where we want to make sure we add to fixed with a two.
[5:54] We also want to make sure we add our currency to the front. For the image, I'm going to make things easier and hop back over to my product slug page, where I'm going to find that image. I'm going to paste that right inside since it'll be the same thing.
[6:07] Similarly, for the button for add to cart, I'm going to do the same exact thing. I'm going to grab that button from my product page. I'm going to replace that category button with the actual button data. Next, we know that we want to link to every single one of these products from the category page.
[6:24] First of all, let's grab this URL that we're generating for that button and replace that href. Since we're using a Next.js application, we want to use client-side routing. To do that, we need to use a Next.js link component.
[6:37] If we look inside of our home page which is similarly listing out products, we can see that we're already importing that component. For all those products that are getting mapped out, we can see right inside of the list item, we're using that link, which is where we pass in the href, where we then nest that A, anchor tag.
[6:53] I'm going to do the same thing. At the top, I'm going to first import my link component. Then, I'm going to wrap this anchor tag with my actual link element, but then I'm going to move that href right up to the actual link component.
[7:08] As one last update to each of these list items, anytime we map through a list like this, we want to make sure that we apply a key to the outermost element. Particularly here is going to be our list item. While we might not run into any bugs here, it's best practice to have that in place, so that React can properly identify each of the elements.
[7:26] I'm going to add my key and I'm going to simply reference product.id. Once the page reloads, we can see that first of all, we do have our title of apparel, but if we scroll down, we see all of our products in that list. We can even add one of those items to the cart, where we see Cosmo hooded sweatshirt.
[7:42] We can navigate to that item where we bring out that dynamic-routed page, where we land on our product page. Now, that we have our dynamically-routed category pages, we can start using those links throughout the site. Such as on the home page, I can now link this banner to the apparel page.
[7:58] Back inside a GraphCMS, I'm going to find that hero link and I'm going to say, categories/apparel. I'm going to hit save and publish. Once we hard refresh and the data reloads, we can now click on our banner and we're going to land on our apparel page.
[8:11] We can also update some of these links inside of the navigation. We're going to save this third link for a future lesson, but we can start updating these other links such as apparel. We can click through those links where we have our accessories page, where we can navigate right back to our apparel page.
[8:27] Finally, now that we have category set up, we have pages for our categories, we want to be able to manage these products on the home page. To do that, we're going to use a category where we already created that featured category.
[8:39] The nice thing is, if we look inside of our index.js home page, we already have a query where we're querying products already. What we want to do is swap which products we're querying. To start, I'm going to go ahead and copy these attributes as we want to query the same things.
[8:54] I'm going to go to my Playground Explorer and I'm going to scroll down and I'm going to still select my products. This time I'm going to select where, and inside of where, we can see that we have a few options, including category specific options, where I want to select categories some.
[9:09] Inside of that, we're going to find fields that relate to specific categories. I'm going to scroll down until we find slug, and I'm going to type in featured for my featured slug and hit play. We can see that we're already getting data back for all the products that are in that featured category.
[9:25] Because I copied those fields for my products, I'm going to paste all of them right in and hit play. We can see that we get all those product fields exactly how we want them. I'm going to go ahead and copy the products part of this query and replace the products query inside of my page home query.
[9:41] Once we hard refresh so that the data repopulates, we can see all of our featured products. In review, we created a new category model where we can reference all of the products that we want in a two-way reference. That means, not only can we reference those products on our category like we worked on, we can go the opposite direction.
[9:59] On our products node, we can reference those categories and get all the categories that relate to a particular product. With that data, we set up a dynamic route for category slug, where we used both getStaticProps and getStaticPaths to build each and every one of those pages, where we were able to dynamically generate each category page with all the products.
Same issue here as before with the following message when I try to run the query in graphcms: "message": "input:1: Field "slug" is not defined by type CategoryWhereUniqueInput.\n"
query PageCategory($slug: String) { category(where: {slug: $slug}) { id name slug products { id mainImage name price slug } } }
{ "slug": "t-shirts" }