Get path name for NextJS route for dynamic routing? - javascript

Is there a way to get the name for the specific route for a page?
I'm getting a list of products and each products gets its own page:
export async function getStaticProps() {
const productCatalog: Array<ProductType> = await getAllProducts()
return { props: { productCatalog } }
}
export async function getStaticPaths() {
const productCatalog: Array<ProductType> = await getAllProducts()
const dynamicFiles: Array<Object> = productCatalog.map(
(product: ProductType) => ({
params: { pid: product.id },
})
)
return {
paths: dynamicFiles,
fallback: false,
}
}
When NextJS generates the static pages, is there a way to get product specific data? I'm currently passing in the entire product catalog in the example then filtering by the single product.
I tried to filter by the route but the router was not recognized. So something like this:
export async function getStaticProps() {
const router: NextRouter = useRouter()
const { pid } = router.query
const productCatalog: Array<ProductType> = await getAllProducts()
const productData: ProductType = productCatalog.filter(
(product: ProductType) => product.id == pid
)[0]
return { props: { productData } }
}

You can access the dynamic page param in getStaticProps through context object's params property. For example, if the page is called [pid].js then you can access context.params.pid:
export async function getStaticProps(context) {
const { pid } = context.params
// Fetch the specific product corresponding to that id
...
}

Related

Dual nested dynamic routing in experimental app directory

I am using NextJS 13 and performing the following inside the app folder.
I am trying to use generateStaticParams function to achieve static generation pages on build.
This is the route: subpage/[categoryName]/[gifId]
So the route could be like following examples.
/subpage/fashion/1
/subpage/fashion/2
/subpage/fashion/3
/subpage/technology/1
/subpage/technology/2
/subpage/technology/3
/subpage/technology/4
... and so on.
The route subpage/[categoryName] won't have anything there. Might show an error or redirect some place.
The full path subpage/[categoryName]/[gifId] including the [gifId] is a must.
I need to perform REST requests to get the data for the pages.
How could I set this up inside my page.tsx file which will be located at: subpage/[categoryName]/[gifId]/page.tsx ?
If it was a single dynamic path, would be straight forward. See my implementation below for that.
But since is nested with 2 dynamic paths [categoryName] and [gifId] back to back, bit confused how to achieve this. Pls assist.
import MyComponent from "../../../components/MyComponent";
import { PartialGifProps, TagType} from "../../../utils/typings";
import axios from "axios";
import {apiDomain, defaultHeaders} from "../../../utils/constants";
const perPage = 40;
type Props = {
params: {
gifId: string,
},
}
export const generateStaticParams = async () => {
const url = `${apiDomain}/get_gif_count`; // I have access to modify the backend for this if it should contain category.
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { total_count: totalCount } : TagType = fetchGifs.data;
const totalPages = Math.ceil(totalCount / perPage);
let paramsList = [];
for (let i = 1; i <= totalPages; i++) {
paramsList.push({ gifId: i.toString() })
}
// this paramsList would look like:
// [
// { gifId: '1', },
// { gifId: '2', },
// { gifId: '3', },
// .......
// ]
return paramsList;
}
const MyPage = async ({params: {gifId}}: Props) => {
const url = `${apiDomain}/get_partial?page=${gifId}&per_page=${perPage}`;
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { gifs } : PartialGifProps = fetchGifs.data;
return (
<div className='text-white'>
<MyComponent gifs={gifs}/>
</div>
);
};
export default MyPage;
You can get categoryName in the same way you get gifId, through the params prop
type Props = {
params: {
gifId: string,
categoryName: string,
},
}
const MyPage = async ({params: {gifId, categoryName}}: Props) => {
console.log('categoryName =', categoryName);
const url = `${apiDomain}/get_partial?page=${gifId}&per_page=${perPage}`;
const fetchGifs = await axios.get(url, { headers: defaultHeaders });
const { gifs } : PartialGifProps = fetchGifs.data;
return (
<div className='text-white'>
<MyComponent gifs={gifs}/>
</div>
);
};

pass variables from front to backend

i have a vue js project with front end and i have a field with variables that i want it when its changed it will go to backend and change there too any idea? the variable is id tage like in this code
app.get('/payments', (request, response) => {
response.set('Access-Control-Allow-Origin','*')
let payments = []
db.collection("payments").where("id", "==", idtag).get().then(snapshot => {
snapshot.forEach((doc) => {
console.log(doc.id, '=>', doc.data())
payments.push(doc.data())
})
response.send(payments)
console.log('payments:',payments)
})
})
and this is the front end
export default defineComponent({
setup () {
const loadinga = ref(false)
const idtag=ref(null)
const filter = ref('')
return {
events: [ '2019/02/01', '2019/02/05', '2019/02/06' ],
date : ref('2019-02-22 21:02'),
columns,
loadinga,
idtag,
Make the ID part of the request url:
// backend assuming this is express
app.get('/payments/:id', (request, response) => {
// use the id in some way
const idtag = req.params.id;
...
});
In your frontend code, you need to watch the idTag and initiate a new request each time it changes.
// frontend
import { defineComponent, ref, watch } from "vue";
export default defineComponent({
setup() {
const idtag = ref(null);
watch(idtag, (newValue, oldValue) => {
fetch(`/payments/${newValue}`)
.then(raw => raw.json())
.then(data => {
// do something with the data
console.log(data)
})
.catch(console.warn);
});
return { idtag };
}
})
If you need this to run immediately, you should use watchEffect instead. But since your value is null at the beginning, I don't think this is the case.

How to show content under a different URL to the file name using next.js

I have the following content structure:
src
|-- content
|-- terms-conditions.html
|-- makeup.html
The terms and conditions page has the following data which I can call
{ seo:
url: "makeup/terms-conditions"
}
I'd like the content of terms-conditions.html to actually show under the url makeup/terms-conditions. I've tried a few times using getStaticPaths to no avail so any guidance would be appreciated. Essentially all of my files will sit one level down from content but I'd like to actually serve them under the URL defined under seo/url
My getStaticPaths code in my [params].tsx file is as follows.
export async function getStaticPaths() {
const posts = getAllPosts(["slug"]);
return {
paths: posts.map((post) => {
return {
params: {
slug: post.slug,
},
};
}),
fallback: false,
};
}
getStaticProps:
export async function getStaticProps({ params }) {
const post = getPostBySlug(params.slug, ["slug", "data"]);
const content = (await post.content) || "";
return {
props: {
post: {
...post,
content,
},
},
};
}
And in my api file I have:
import fs from "fs";
import { join } from "path";
import matter from "gray-matter";
const postsDirectory = join(process.cwd(), "src/content");
export function getPostSlugs() {
return fs.readdirSync(postsDirectory);
}
export function getPostBySlug(slug: string, fields = []) {
const realSlug = slug.replace(/\.html$/, "");
const fullPath = join(postsDirectory, `${realSlug}.html`);
const fileContents = fs.readFileSync(fullPath, "utf8");
const { data, content } = matter(fileContents);
const items = { };
fields.forEach((field) => {
if (field === 'slug') {
items[field] = realSlug
}
if (field === 'data') {
items[field] = data
}
})
return items;
}
export function getAllPosts(fields = []) {
const slugs = getPostSlugs()
const posts = slugs
.map((slug) => getPostBySlug(slug, fields))
return posts
}

getStaticPaths() returns 404 Page Not Found

The getStaticPaths() keeps throwing the 404 page and I'm guessing that is because of the function getAllPostIds() which is meant to return an array of objects. I'm making use of typescript, I've declared the array of objects but I'm not sure how to use the replace method on the id
interface ArrObj {
params: {
id: string;
}
}
let arrObj: ArrObj[];
export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory);
return fileNames.map(fileName => {
return {
params: {
id: fileName.replace(/\.md$/, '')
}
}
})
}
export function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`)
const fileContents = fs.readFileSync(fullPath, 'utf8')
const matterResult = matter(fileContents)
return {
id,
...matterResult.data
}
}
export async function getStaticPaths(){
const paths = getAllPostIds();
return{
paths,
fallback: false
}
}
export async function getStaticProps({ params }) {
const postData = getPostData(params.id)
return {
props: {
postData
}
}
}
Add the fallback props as true or blocking. Seting it as false will return a 404 page because the requested path was not generated during build time.
return{
paths,
fallback: true
}

Getting "A required parameter (id) was not provided as a string in getStaticPaths" error in Next.js

I have a problem with the getStaticPaths function.
When I try to get a dynamic display with a parameter it shows me as error: A required parameter (id) was not provided as a string in getStaticPaths for / movies / [id] but if I use the other way above it works. Above all I am the documentation.
import fetch from 'node-fetch';
function MovieSelect({movie}){
return(
<div>
<h1>Test: {movie.name}</h1>
<p>{movie.summary.replace(/<[/]?[pb]>/g, '')}</p>
{movie.image ? <img src={movie.image.medium} /> : null}
</div>
)
}
export async function getStaticPaths(){
const request = await fetch('https://api.tvmaze.com/search/shows?q=batman')
const movies = await request.json()
//const paths = movies.map(movie =>`/movies/${movie.show.id}`)
const paths = movies.map(movie =>({
params: {id: movie.show.id},
}))
return {
paths,
fallback: false
}
}
export async function getStaticProps({params}){
const request = await fetch(`https://api.tvmaze.com/shows/${params.id}`)
const movie = await request.json()
return{
props:{
movie
}
}
}
export default MovieSelect
A required parameter (id) was not provided as a string in getStaticPaths for / movies / [id]
id should be a string as suggested by the error. Upon hitting the api from your browser, you can see that the id is not a string but a number. You need to convert it to string.
params: {id: movie.show.id.toString()},
My problem generated the same error, but I had a different bug.
TL;DR: The name of my file needed to match the key of the slug used in the params object.
In my case, my file name was [postSlug].js. Therefore, the key should have been postSlug inside of getStaticPaths().
// In [postSlug].js
const pathsWithParams = slugs.map((slugs) => ({ params: { postSlug: slug } })); // <-- postSlug is right
const pathsWithParams = slugs.map((slugs) => ({ params: { id: slug } })); // <--- id is wrong
My entire function then looked like this
export async function getStaticPaths() {
const slugs = await getAllBlogSlugs();
const pathsWithParams = slugs.map((slug) => ({ params: { postSlug: slug } }));
return {
paths: pathsWithParams,
fallback: "blocking",
};
}
References:
NextJS.org Get Static Paths
export async function getServerSideProps({ query, locale }) {
const { id } = query;
if (!id) return { notFound: true };
return {
props: {
fallback: true,
query,
locale,
...(await serverSideTranslations(
locale,
["common", "header", "footer"],
nextI18nextConfig
)),
},
};
}

Categories

Resources