ratty-blush
ratty-blushā€¢16mo ago

what is the correct way to list media in the interface export when using strapi v4 media uploader?

if I am using strapi with the media uploader, what would the correct way to list that media in the interface export?
35 Replies
kapa.ai
kapa.aiā€¢16mo ago
Hi @erekyu. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To list media in the interface when using Strapi with Refine, you can follow these steps: 1. Create an interface for the data from your Strapi API, including the media field. For example, if you have a Product collection with an Image field, your interface might look like this:
export interface IProduct {
id: number;
title: string;
description: string;
image: {
url: string;
};
}
export interface IProduct {
id: number;
title: string;
description: string;
image: {
url: string;
};
}
2. In your list component, use the useDataGrid hook to fetch the data and display it in a table. For example, in a ProductList component:
import { useDataGrid } from "@pankod/refine";

const { dataGridProps } = useDataGrid<IProduct>();
import { useDataGrid } from "@pankod/refine";

const { dataGridProps } = useDataGrid<IProduct>();
3. Define the columns for your table, including a column for the media field. To display the image, you can use the renderCell function:
import { GridColumns } from "@mui/x-data-grid";

const columns = React.useMemo<GridColumns<IProduct>>(
() => [
// Other columns...
{
field: "image",
headerName: "Image",
minWidth: 150,
renderCell: function render({ row }) {
return <img src={row.image?.url} alt={row.title} width="100" />;
},
},
// Other columns...
],
[]
);
import { GridColumns } from "@mui/x-data-grid";

const columns = React.useMemo<GridColumns<IProduct>>(
() => [
// Other columns...
{
field: "image",
headerName: "Image",
minWidth: 150,
renderCell: function render({ row }) {
return <img src={row.image?.url} alt={row.title} width="100" />;
},
},
// Other columns...
],
[]
);
4. Finally, use the DataGrid component to display the table with the fetched data and defined columns:
import { DataGrid } from "@mui/x-data-grid";

<DataGrid {...dataGridProps} columns={columns} />;
import { DataGrid } from "@mui/x-data-grid";

<DataGrid {...dataGridProps} columns={columns} />;
This will display the media (e.g., images) in the interface when listing the records. For more information on listing records with Refine and Strapi, you can refer to the following sources:
kapa.ai
kapa.aiā€¢16mo ago
- How to Build a React Admin Panel with Mantine and Strapi (https://refine.dev/blog/react-admin-panel/) - Building a CRUD app with Material UI and Strapi in React (https://refine.dev/blog/build-admin-panel-with-material-ui-and-strapi/) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
How to Build a React Admin Panel with Mantine and Strapi | refine
We'll be building a simple React Admin Panel with refine and Mantine using Strapi as a backend service.
Building a CRUD app with Material UI and Strapi in React | refine
How to build CRUD admin panel with Material UI?
kapa.ai
kapa.aiā€¢16mo ago
<@66380739771379712> kapa.ai is still learning and improving, please let us how it did by reacting below
ratty-blush
ratty-blushā€¢16mo ago
@kapa.ai what would the edit page look like
kapa.ai
kapa.aiā€¢16mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The edit page for a record in a Refine application would typically include a form with input fields for each property of the record. Here's an example of what an edit page might look like for a Post resource: 1. Create a PostEdit component in your pages/posts directory:
// src/pages/posts/edit.tsx

import { Edit, IResourceComponentsProps, Input, useForm } from "@pankod/refine";

export const PostEdit: React.FC<IResourceComponentsProps> = () => {
const { formProps, saveButtonProps } = useForm();

return (
<Edit saveButtonProps={saveButtonProps}>
<Input name="title" label="Title" {...formProps} />
<Input name="content" label="Content" {...formProps} />
{/* Add other input fields as needed */}
</Edit>
);
};
// src/pages/posts/edit.tsx

import { Edit, IResourceComponentsProps, Input, useForm } from "@pankod/refine";

export const PostEdit: React.FC<IResourceComponentsProps> = () => {
const { formProps, saveButtonProps } = useForm();

return (
<Edit saveButtonProps={saveButtonProps}>
<Input name="title" label="Title" {...formProps} />
<Input name="content" label="Content" {...formProps} />
{/* Add other input fields as needed */}
</Edit>
);
};
2. Import the PostEdit component in your pages/posts/index.tsx file:
// src/pages/posts/index.tsx

export * from "./edit";
// src/pages/posts/index.tsx

export * from "./edit";
3. Add the PostEdit component to the resources prop of the Refine component in your App.tsx file:
// src/App.tsx

import {
PostList,
PostCreate,
PostEdit,
} from "pages/posts";

// ...

const App: React.FC = () => {
return (
// ...
<Refine
// ...
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
},
]}
/>
// ...
);
};
// src/App.tsx

import {
PostList,
PostCreate,
PostEdit,
} from "pages/posts";

// ...

const App: React.FC = () => {
return (
// ...
<Refine
// ...
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
},
]}
/>
// ...
);
};
After completing these steps, clicking the edit button for a post should redirect you to the edit page, where you can update the record data. For more information on creating an edit page with Refine and Strapi, you can refer to the following sources:
kapa.ai
kapa.aiā€¢16mo ago
- How to Build a React Admin Panel with Mantine and Strapi (https://refine.dev/blog/react-admin-panel/) - Building a CRUD app with Material UI and Strapi in React (https://refine.dev/blog/build-admin-panel-with-material-ui-and-strapi/) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
How to Build a React Admin Panel with Mantine and Strapi | refine
We'll be building a simple React Admin Panel with refine and Mantine using Strapi as a backend service.
Building a CRUD app with Material UI and Strapi in React | refine
How to build CRUD admin panel with Material UI?
kapa.ai
kapa.aiā€¢16mo ago
<@66380739771379712> kapa.ai is still learning and improving, please let us how it did by reacting below
ratty-blush
ratty-blushā€¢16mo ago
@kapa.ai no what I mean is what would the edit page look like when using the strapi media uploader and using export interface to list the media
kapa.ai
kapa.aiā€¢16mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To create an edit page with a Strapi media uploader, you can use the Upload.Dragger component from Ant Design. Here's an example of an edit page for a Post resource with an image field: 1. Create an interface for the Post resource, including the image field:
// src/interfaces.ts

export interface IPost {
id: string;
title: string;
image: {
url: string;
};
}
// src/interfaces.ts

export interface IPost {
id: string;
title: string;
image: {
url: string;
};
}
2. Create a PostEdit component in your pages/posts directory:
// src/pages/posts/edit.tsx

import {
Edit,
useForm,
getValueFromEvent,
} from "@pankod/refine";
import { Form, Input, Upload } from "antd";
import { useApiUrl } from "@pankod/refine";
import { IPost } from "interfaces";

export const PostEdit: React.FC = () => {
const { formProps, saveButtonProps } = useForm<IPost>();
const apiUrl = useApiUrl();

return (
<Edit saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item label="Title" name="title" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={1}
multiple={false}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
);
};
// src/pages/posts/edit.tsx

import {
Edit,
useForm,
getValueFromEvent,
} from "@pankod/refine";
import { Form, Input, Upload } from "antd";
import { useApiUrl } from "@pankod/refine";
import { IPost } from "interfaces";

export const PostEdit: React.FC = () => {
const { formProps, saveButtonProps } = useForm<IPost>();
const apiUrl = useApiUrl();

return (
<Edit saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item label="Title" name="title" rules={[{ required: true }]}>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={getValueFromEvent}
noStyle
>
<Upload.Dragger
name="file"
action={`${apiUrl}/media/upload`}
listType="picture"
maxCount={1}
multiple={false}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
);
};
3. Import the PostEdit component in your pages/posts/index.tsx file:
// src/pages/posts/index.tsx

export * from "./edit";
// src/pages/posts/index.tsx

export * from "./edit";
kapa.ai
kapa.aiā€¢16mo ago
4. Add the PostEdit component to the resources prop of the Refine component in your App.tsx file:
// src/App.tsx

import {
PostList,
PostCreate,
PostEdit,
} from "pages/posts";

// ...

const App: React.FC = () => {
return (
// ...
<Refine
// ...
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
},
]}
/>
// ...
);
};
// src/App.tsx

import {
PostList,
PostCreate,
PostEdit,
} from "pages/posts";

// ...

const App: React.FC = () => {
return (
// ...
<Refine
// ...
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
},
]}
/>
// ...
);
};
This example demonstrates how to create an edit page with a Strapi media uploader for a Post resource with an image field. The Upload.Dragger component allows you to upload images to Strapi. For more information on creating an edit page with a media uploader, you can refer to the following source: - Multipart Upload - Refine Documentation (https://refine.dev/docs/advanced-tutorials/upload/multipart-upload) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
Multipart Upload | refine
We will show you how to multipart upload with refine.
kapa.ai
kapa.aiā€¢16mo ago
<@66380739771379712> kapa.ai is still learning and improving, please let us how it did by reacting below
ratty-blush
ratty-blushā€¢16mo ago
@kapa.ai when i do that for my edit page I get the following error "e is not iterable TypeError: e is not iterable"
kapa.ai
kapa.aiā€¢16mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- It seems like you're encountering an issue with the getValueFromEvent function. This function is used to handle the file upload event and extract the necessary information. The error you're encountering might be due to an incorrect implementation of this function. To fix the issue, you can use the getValueFromEvent function provided by Refine. Update your PostEdit component as follows:
// src/pages/posts/edit.tsx

import {{
Edit,
useForm,
getValueFromEvent, // Import this function from Refine
}} from "@pankod/refine";
import {{ Form, Input, Upload }} from "antd";
import {{ useApiUrl }} from "@pankod/refine";
import {{ IPost }} from "interfaces";

export const PostEdit: React.FC = () => {{
const {{ formProps, saveButtonProps }} = useForm<IPost>();
const apiUrl = useApiUrl();

return (
<Edit saveButtonProps={{saveButtonProps}}>
<Form {{...formProps}} layout="vertical">
<Form.Item label="Title" name="title" rules={{[{{ required: true }}]}}>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={{getValueFromEvent}} // Use the imported function
noStyle
>
<Upload.Dragger
name="file"
action={{`${{apiUrl}}/media/upload`}}
listType="picture"
maxCount={{1}}
multiple={{false}}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
);
}};
// src/pages/posts/edit.tsx

import {{
Edit,
useForm,
getValueFromEvent, // Import this function from Refine
}} from "@pankod/refine";
import {{ Form, Input, Upload }} from "antd";
import {{ useApiUrl }} from "@pankod/refine";
import {{ IPost }} from "interfaces";

export const PostEdit: React.FC = () => {{
const {{ formProps, saveButtonProps }} = useForm<IPost>();
const apiUrl = useApiUrl();

return (
<Edit saveButtonProps={{saveButtonProps}}>
<Form {{...formProps}} layout="vertical">
<Form.Item label="Title" name="title" rules={{[{{ required: true }}]}}>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={{getValueFromEvent}} // Use the imported function
noStyle
>
<Upload.Dragger
name="file"
action={{`${{apiUrl}}/media/upload`}}
listType="picture"
maxCount={{1}}
multiple={{false}}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
);
}};
By using the getValueFromEvent function provided by Refine, you should be able to resolve the "e is not iterable" error.
kapa.ai
kapa.aiā€¢16mo ago
For more information on handling file uploads with Refine, you can refer to the following source: - Multipart Upload - Refine Documentation (https://refine.dev/docs/advanced-tutorials/upload/multipart-upload) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
Multipart Upload | refine
We will show you how to multipart upload with refine.
kapa.ai
kapa.aiā€¢16mo ago
<@66380739771379712> kapa.ai is still learning and improving, please let us how it did by reacting below
ratty-blush
ratty-blushā€¢16mo ago
@kapa.ai my new error for getValueFromEvent is "(fileList []).forEach is not a function TypeError: (fileList []).forEach is not a function"
kapa.ai
kapa.aiā€¢16mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- It seems like the getValueFromEvent function is expecting an array, but it's receiving a non-array value. To fix this issue, you can create a custom getValueFromEvent function that handles the file upload event and ensures that the returned value is an array. Update your PostEdit component with a custom getValueFromEvent function as follows:
// src/pages/posts/edit.tsx

import {{
Edit,
useForm,
}} from "@pankod/refine";
import {{ Form, Input, Upload }} from "antd";
import {{ useApiUrl }} from "@pankod/refine";
import {{ IPost }} from "interfaces";

export const PostEdit: React.FC = () => {{
const {{ formProps, saveButtonProps }} = useForm<IPost>();
const apiUrl = useApiUrl();

// Custom getValueFromEvent function
const customGetValueFromEvent = (e: any) => {{
if (Array.isArray(e)) {{
return e;
}}
return e && e.fileList ? e.fileList : [];
}};

return (
<Edit saveButtonProps={{saveButtonProps}}>
<Form {{...formProps}} layout="vertical">
<Form.Item label="Title" name="title" rules={{[{{ required: true }}]}}>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={{customGetValueFromEvent}} // Use the custom function
noStyle
>
<Upload.Dragger
name="file"
action={{`${{apiUrl}}/media/upload`}}
listType="picture"
maxCount={{1}}
multiple={{false}}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
);
}};
// src/pages/posts/edit.tsx

import {{
Edit,
useForm,
}} from "@pankod/refine";
import {{ Form, Input, Upload }} from "antd";
import {{ useApiUrl }} from "@pankod/refine";
import {{ IPost }} from "interfaces";

export const PostEdit: React.FC = () => {{
const {{ formProps, saveButtonProps }} = useForm<IPost>();
const apiUrl = useApiUrl();

// Custom getValueFromEvent function
const customGetValueFromEvent = (e: any) => {{
if (Array.isArray(e)) {{
return e;
}}
return e && e.fileList ? e.fileList : [];
}};

return (
<Edit saveButtonProps={{saveButtonProps}}>
<Form {{...formProps}} layout="vertical">
<Form.Item label="Title" name="title" rules={{[{{ required: true }}]}}>
<Input />
</Form.Item>
<Form.Item label="Image">
<Form.Item
name="image"
valuePropName="fileList"
getValueFromEvent={{customGetValueFromEvent}} // Use the custom function
noStyle
>
<Upload.Dragger
name="file"
action={{`${{apiUrl}}/media/upload`}}
listType="picture"
maxCount={{1}}
multiple={{false}}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
);
}};
kapa.ai
kapa.aiā€¢16mo ago
By using the custom getValueFromEvent function, you should be able to resolve the "(fileList || []).forEach is not a function" error. For more information on handling file uploads with Refine, you can refer to the following source: - Multipart Upload - Refine Documentation (https://refine.dev/docs/advanced-tutorials/upload/multipart-upload) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
Multipart Upload | refine
We will show you how to multipart upload with refine.
kapa.ai
kapa.aiā€¢16mo ago
<@66380739771379712> kapa.ai is still learning and improving, please let us how it did by reacting below
ratty-blush
ratty-blushā€¢16mo ago
@kapa.ai still getting the same error
kapa.ai
kapa.aiā€¢16mo ago
Let me check the knowledge base for you. One moment! :books: @erekyu That's a great question, but unfortunately I am only capable of responding to a maximum of three questions per thread. Don't hesitate to create a new thread for any further questions you have though. I'm here to help!
fascinating-indigo
fascinating-indigoā€¢16mo ago
Hey @erekyu, https://github.com/refinedev/refine/pull/4196 I've created a PR to resolve file upload issues with refine. Now, It's on the review process. But, you can see the changes on PR like documentation, fixed examples and others. I hope it helps you šŸ™
ratty-blush
ratty-blushā€¢16mo ago
do you think that is also the reason its not listing files that already exist on the edit page?
fascinating-indigo
fascinating-indigoā€¢16mo ago
Yes, I do. As far as I understand, the strapi API has changed and our getValueProps method is outdated. I updated it and problem solved on the example.
ratty-blush
ratty-blushā€¢16mo ago
You are a rock star, I thought I was doing something wrong this hole time! I went over every example deleted and reinstalled and reset up strapi and refine multiple times I thought I was going crazy
fascinating-indigo
fascinating-indigoā€¢16mo ago
Frankly, I went through similar processes while I was doing it šŸ¤£
ratty-blush
ratty-blushā€¢16mo ago
did you ever run into
e is not iterable
TypeError: e is not iterable
e is not iterable
TypeError: e is not iterable
because I made all the changes and the files upload, and attach perfectly but when i go back into the edit i get
e is not iterable
TypeError: e is not iterable
at G (http://localhost:3000/static/js/bundle.js:36243:76)
at getValueProps (http://localhost:3000/static/js/bundle.js:1867:105)
at Field._this.getControlled (http://localhost:3000/static/js/bundle.js:208373:208)
at Field._this.getOnlyChild (http://localhost:3000/static/js/bundle.js:208328:224)
at Field.render (http://localhost:3000/static/js/bundle.js:208470:37)
at finishClassComponent (http://localhost:3000/static/js/bundle.js:266235:35)
at updateClassComponent (http://localhost:3000/static/js/bundle.js:266192:28)
at beginWork (http://localhost:3000/static/js/bundle.js:267818:20)
at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:252809:18)
at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:252853:20)
e is not iterable
TypeError: e is not iterable
at G (http://localhost:3000/static/js/bundle.js:36243:76)
at getValueProps (http://localhost:3000/static/js/bundle.js:1867:105)
at Field._this.getControlled (http://localhost:3000/static/js/bundle.js:208373:208)
at Field._this.getOnlyChild (http://localhost:3000/static/js/bundle.js:208328:224)
at Field.render (http://localhost:3000/static/js/bundle.js:208470:37)
at finishClassComponent (http://localhost:3000/static/js/bundle.js:266235:35)
at updateClassComponent (http://localhost:3000/static/js/bundle.js:266192:28)
at beginWork (http://localhost:3000/static/js/bundle.js:267818:20)
at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:252809:18)
at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:252853:20)
fascinating-indigo
fascinating-indigoā€¢16mo ago
Can you share your API response on edit page?
ratty-blush
ratty-blushā€¢16mo ago
if i remove
valuePropName='fileList'
getValueProps={(data) => getValueProps(data, API_URL)}
valuePropName='fileList'
getValueProps={(data) => getValueProps(data, API_URL)}
i can get the page to load, this is the response to my api call for the file i am trying to get:
contract 18 items
id: 17
name: "testFile.pdf"
alternativeText: null
caption: null
width: null
height: null
formats: null
hash: "43e9b4f7340f98200b70b74021342391"
ext: ".pdf"
mime: "application/pdf"
size: 342.36
url: "https://mmgi.nyc3.cdn.digitaloceanspaces.com/tsd/43e9b4f7340f98200b70b74021342391.pdf"
previewUrl: null
provider: "strapi-provider-upload-dos"
provider_metadata: null
createdAt: "2023-04-18T20:19:53.923Z"
updatedAt: "2023-04-18T20:19:53.923Z"
related 1 item
ā–¶ 0 9 items
id: 1
__type: "api::project.project"
address: "123 test st"
city: "Boynton beach"
zipCode: "33426"
projectType: "Pool Enclosure"
createdAt: "2023-04-07T13:54:55.360Z"
updatedAt: "2023-04-18T20:19:58.417Z"
contractPrice: "30,000.00"
contract 18 items
id: 17
name: "testFile.pdf"
alternativeText: null
caption: null
width: null
height: null
formats: null
hash: "43e9b4f7340f98200b70b74021342391"
ext: ".pdf"
mime: "application/pdf"
size: 342.36
url: "https://mmgi.nyc3.cdn.digitaloceanspaces.com/tsd/43e9b4f7340f98200b70b74021342391.pdf"
previewUrl: null
provider: "strapi-provider-upload-dos"
provider_metadata: null
createdAt: "2023-04-18T20:19:53.923Z"
updatedAt: "2023-04-18T20:19:53.923Z"
related 1 item
ā–¶ 0 9 items
id: 1
__type: "api::project.project"
address: "123 test st"
city: "Boynton beach"
zipCode: "33426"
projectType: "Pool Enclosure"
createdAt: "2023-04-07T13:54:55.360Z"
updatedAt: "2023-04-18T20:19:58.417Z"
contractPrice: "30,000.00"
oh i missed part ill edit that
ratty-blush
ratty-blushā€¢16mo ago
yes
fascinating-indigo
fascinating-indigoā€¢16mo ago
I can't quite understand here, but is your file field an array or an object? Are you sure you are using your own getValueProps function? Or are you importing from @refinedev/strapi-v4? Because the changes in PR have not yet been released
ratty-blush
ratty-blushā€¢16mo ago
i copied your changes into my node_modules (removed the files and updated the array)
fascinating-indigo
fascinating-indigoā€¢16mo ago
The error is caused by trying to spread an object in an array. Can you try creating an own getValueProps function and use it. In my opinion node_modules changes don't work
ratty-blush
ratty-blushā€¢16mo ago
sure i will try that driving home now will test it later this evening, ty again for your time i apricate it that did it ty sooo much!