Display single element
We previously create a table of element, but what if we want to display all details of an unique element ?
Here we will see how to access a single article from articles table.
Add dynamic pages
Defining routes by using predefined paths is not always enough for complex applications. In Next.js you can add brackets to a page ([param]) to create a dynamic route (a.k.a. url slugs, pretty urls, and others).
We will use NextJS's dynamic routes. Find out more here
For articles we created an article folder in page folder with only one file [id].tsx. This file is a dynamic page working as a template that will change it's value for a desire ID.
Send ID to path query
In order to get an ID from the path, we need to add the action column in the Articles Table.
import { LinkButton } from "@components";
import { pathParams } from "@helpers";
const columns = [
{ ...otherColumns },
{
title: "actions:actions",
key: "actions",
render: (record: { id: string }) => (
<Space>
<LinkButton
icon={<EyeTwoTone />}
path={pathParams("/article/[id]", record.id)}
/>
</Space>
),
},
];
A render key is passed, you can get all values for each rows with the record property but you need to make sure you have in the data fecthed.
Here we get the id of each data for a specific row. Then we used the LinkButton to make a link to the desired article. The pathParams utils get a path to an url to go and an ID query parameter. On click it will automatically redirect the user to /article/idOfTheRow.
Retrive ID
import { AppHead } from "@components";
import { SingleArticle } from "modules/Articles/PagesContainer/SingleArticle";
import { useRouter } from "next/router";
import { FC } from "react";
import MainLayout from "../../components/layouts/MainLayout";
type PageComponent = FC & { layout: typeof MainLayout };
const ArticlePage: PageComponent = () => {
const router = useRouter();
const { id } = router.query;
return (
<>
<AppHead title="Bee V2" />
<SingleArticle router={router} id={id!} />
</>
);
};
ArticlePage.layout = MainLayout;
export default ArticlePage;
In order to get the right ID in the template we used NextJS Router. We will then pass down the id get by router.query to a PageContainer. You can also pass the router if you want a go back button to the HeaderContent component in the PageContainer, here SingleArticle.tsx.
Get the data
import { GetArticleByIdQuery, useGetArticleByIdQuery } from "generated/graphql";
import { useAuth } from "context/AuthContext";
const { graphqlRequestClient } = useAuth();
const { isLoading, data, error } = useGetArticleByIdQuery<
GetArticleByIdQuery,
Error
>(graphqlRequestClient, {
id: parseInt(id),
});
const breadsCrumb = [
...articlesSubRoutes,
{
breadcrumbName: `${id}`,
},
];
We trigger a query with the specific ID and we used parseInt(id) to convert the query params which is a string to an Int.
return (
<>
<HeaderContent
title={`${t("common:article")} ${id}`}
routes={breadsCrumb}
onBack={() => router.back()} // go back button
/>
<StyledPageContent>
{data?.article && !isLoading ? (
<ArticleDetails details={data?.article} />
) : (
<ContentSpin />
)}
</StyledPageContent>
</>
);
Then we just pass down fetched data child component.
Display all element details
Now it's time to display all details for the retrieved ID. In Elements folder we created an ArticleDetails.tsx file.
In article detail we want to display all details but also all barcodes associated with it.
So we need to get all associated barcodes for a specific article. Then it will be display in a Ant Design Table Component.
import { DetailsList, LinkButton, ContentSpin, AppTable } from "@components";
import { EyeTwoTone } from "@ant-design/icons";
import {
pathParams,
useBarcodes,
DataQueryType,
PaginationType,
DEFAULT_ITEMS_PER_PAGE,
DEFAULT_PAGE_NUMBER,
} from "@helpers";
import useTranslation from "next-translate/useTranslation";
import { Divider, Typography } from "antd";
import { useState, useEffect, useCallback } from "react";
const { Title } = Typography;
export interface IArticleDetailsProps {
details?: any;
}
const ArticleDetails = ({ details }: IArticleDetailsProps) => {
const { t } = useTranslation();
const [barcodes, setBarcodes] = useState<DataQueryType>();
const [pagination, setPagination] = useState<PaginationType>({
total: undefined,
current: DEFAULT_PAGE_NUMBER,
itemsPerPage: DEFAULT_ITEMS_PER_PAGE,
});
const { isLoading, data, error } = useBarcodes(
{ articleId: parseInt(details.id) },
pagination.current,
pagination.itemsPerPage,
null
);
// make wrapper function to give child
const onChangePagination = useCallback(
(currentPage, itemsPerPage) => {
// Re fetch data for new current page or items per page
setPagination({
total: barcodes?.count,
current: currentPage,
itemsPerPage: itemsPerPage,
});
},
[setPagination, barcodes]
);
const barcodeColumns = [
{
title: t("d:id"),
dataIndex: "id",
key: "id",
},
{
title: t("common:name"),
dataIndex: "name",
key: "name",
},
{
title: t("d:articleId"),
dataIndex: "articleId",
key: "articleId",
},
{
title: t("d:flagDouble"),
dataIndex: "flagDouble",
key: "flagDouble",
},
{
title: t("actions:actions"),
key: "actions",
render: (record: { id: string }) => (
<LinkButton
icon={<EyeTwoTone />}
path={pathParams("/barcode/[id]", record.id)}
/>
),
},
];
// For pagination
useEffect(() => {
if (data) {
setBarcodes(data?.barcodes);
setPagination({
...pagination,
total: data?.barcodes?.count,
});
}
}, [data]);
return (
<>
<DetailsList details={details} />
<Divider />
<Title level={4}>
{t("common:associated", { name: t("common:barcodes") })}
</Title>
{barcodes ? (
<AppTable
type="associatedBarcodes"
columns={barcodeColumns}
data={barcodes!.results}
pagination={pagination}
isLoading={isLoading}
setPagination={onChangePagination}
filter={false}
/>
) : (
<ContentSpin />
)}
</>
);
};
export { ArticleDetails };
The DetailsList component will display respectively the key and the value of the article object by passing the object. All properties of DetailsList
In order to automatically display the best translation we used the d.json namespaces. It should be used only for translating all data object field.s