vicious-gold
vicious-gold14mo ago

How does one supported nested resource

Considering the following resources
const resources = [
{
name: "customers",
list: "/customers",
create: "/customers/create",
edit: "/customers/:id/edit",
show: "/customers/:id/show",
meta: {
canDelete: true,
},
},
{
name: "customer-products",
list: "/customers/:customer_id/products",
create: "/customers/:customer_id/products/create",
edit: "/customers/:customer_id/products/:id/edit",
show: "/customers/:customer_id/products/:id/show",
meta: {
canDelete: true,
},
options: {
hide: true,
},
},
];
const resources = [
{
name: "customers",
list: "/customers",
create: "/customers/create",
edit: "/customers/:id/edit",
show: "/customers/:id/show",
meta: {
canDelete: true,
},
},
{
name: "customer-products",
list: "/customers/:customer_id/products",
create: "/customers/:customer_id/products/create",
edit: "/customers/:customer_id/products/:id/edit",
show: "/customers/:customer_id/products/:id/show",
meta: {
canDelete: true,
},
options: {
hide: true,
},
},
];
How can this be achieved ? If I change from :customer_id to :id for list and create of customer-products then all is fine with the list and create functionality, but I can't use it for edit and show buttons. As it will be the same as the product_id, not the desired customer_id How do you deal with this problem ?
7 Replies
kapa.ai
kapa.ai14mo ago
Hi @i.stoica. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Refine supports route parameters defined with :param syntax. You can use these parameters in your action definitions and create your routes accordingly. For example, if you have a posts resource and you want to create a route for the show action of a specific post, you can define the show action as /posts/show/:id and use the id parameter in your component. Your action definitions in the resources can contain additional parameters and nested routes. Passing these parameters when navigating to the pages are handled by the current available parameters and the meta props of the related hooks and components. However, based on the information provided in the extracts, it seems that Refine does not directly support nested resources in the way you're trying to implement them. The :id parameter is typically used to identify a specific resource, not a parent resource. One possible workaround could be to store the selected :store_id (from a Select input, for example), and passing it as a parameter to the resource. This would likely involve creating a Custom Page and implementing the API request with axios or something similar. For more information, you can refer to the Refine documentation on routing (https://refine.dev/docs/routing/integrations/react-router#usage-with-a-root-route) and Refine documentation on Remix (https://refine.dev/docs/routing/integrations/remix#usage). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai14mo ago
<@180601470268211200> kapa.ai is still learning and improving, please let us know how it did by reacting below
vicious-gold
vicious-goldOP14mo ago
The example shown has resources at same level, no nesting
rare-sapphire
rare-sapphire13mo ago
@i.stoica Hi, I'm having the same problem, did you manage to find a solution?
sensitive-blue
sensitive-blue13mo ago
Has anyone tried nested resources with nextJS Router "@refinedev/nextjs-router" ? Any references/example will be super helpful.
vicious-gold
vicious-goldOP12mo ago
Yes, nesting the routing and the using a custom parameter extractor with useMatch - I think it is very ugly but works - if anyoneelse knows a better way (except Kappa :D)
const { resource } = useResource();
const match = useMatch(`${resource?.edit}`);
const customer_id = match?.params.customer_id ?? "";
const product_id = match?.params.product_id ?? "";
const meta = useMemo(
() => ({
customer_id,
product_id,
}),
[customer_id, product_id]
);

const { data: customer, isLoading: customerIsLoading } = useOne({
resource: "customers",
id: customer_id,
});
const { resource } = useResource();
const match = useMatch(`${resource?.edit}`);
const customer_id = match?.params.customer_id ?? "";
const product_id = match?.params.product_id ?? "";
const meta = useMemo(
() => ({
customer_id,
product_id,
}),
[customer_id, product_id]
);

const { data: customer, isLoading: customerIsLoading } = useOne({
resource: "customers",
id: customer_id,
});
deep-jade
deep-jade4w ago
@swapnil-marble Taking the example of @i.stoica you should invert :id in customers and products routes, otherwise, you'll end with 2 slugs for same route which breaks nextjs router with error Failed to reload dynamic routes: Error: You cannot use different slug names for the same dynamic path ('customer_id' !== 'id').
const resources = [
{
name: "customers",
list: "/customers",
create: "/customers/create",
edit: "/customers/edit/:id",
show: "/customers/show/:id",
meta: {
canDelete: true,
},
},
{
name: "customer-products",
list: "/customers/:customer_id/products",
create: "/customers/:customer_id/products/create",
edit: "/customers/:customer_id/products/edit/id",
show: "/customers/:customer_id/products/show/:id",
meta: {
canDelete: true,
},
options: {
hide: true,
},
},
];
const resources = [
{
name: "customers",
list: "/customers",
create: "/customers/create",
edit: "/customers/edit/:id",
show: "/customers/show/:id",
meta: {
canDelete: true,
},
},
{
name: "customer-products",
list: "/customers/:customer_id/products",
create: "/customers/:customer_id/products/create",
edit: "/customers/:customer_id/products/edit/id",
show: "/customers/:customer_id/products/show/:id",
meta: {
canDelete: true,
},
options: {
hide: true,
},
},
];
Then, define your folders as
/customers/edit/[id]
/customers/show/[id]
/customers/[customer_id]/products
/customers/[customer_id]/products/edit/[id]
/customers/[customer_id]/products/show/[id]
/customers/edit/[id]
/customers/show/[id]
/customers/[customer_id]/products
/customers/[customer_id]/products/edit/[id]
/customers/[customer_id]/products/show/[id]
Then, in your products pages (show, list, edit, create), retrieve customer_id using
const { params } = useParsed();
params?.customer_id
const { params } = useParsed();
params?.customer_id
And if you use ShowButton, EditButton, ... for products, pass the customer_id param as meta, ex for show product button :
<ShowButton
hideText
size="small"
recordItemId={product.id}
meta={{
customer_id: params?.customer_id,
}}
title="Show Product"
/>
<ShowButton
hideText
size="small"
recordItemId={product.id}
meta={{
customer_id: params?.customer_id,
}}
title="Show Product"
/>
Hope this helps, I struggled too with this...

Did you find this page helpful?