Skip to main content

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.

ArticlesList.tsx - Actions Columns
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

ArticlesList.tsx - Actions Columns
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

SingleArticle.tsx - Query
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.

SingleArticle.tsx - Query
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.

ArticleDetails.tsx - Barcodes
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

Learn more about d.json