Abbasali Rezaei
Abbasali Rezaei

Reputation: 13

Add multiple category for one post (drf+React)

I have a blog website built with Django and React. I'm currently working on adding a new blog post feature, and I want to allow users to select one or multiple categories for their blog post. Here's what I have done so far:

  1. Fetched all the categories from the database and displayed them in a dropdown/select field.

  2. Users can select one or multiple categories from the dropdown.

Now, I need help with the next steps. How can I send the selected categories to the database and save them for the new blog post? Any guidance or code examples would be greatly appreciated.

import React, { useEffect, useState } from 'react';
import Dashboard from './Dashboard';
import useAxios from '../../utils/useAxios';
function AuthorCreateBlogs() {
    const baseurl = "http://127.0.0.1:8000/api/posts/v1";
    const [selectedCategories, setSelectedCategories] = useState([]);
    const api = useAxios();
    const [categoriesData, setCategoriesData] = useState([]);
    const [blogData, setBlogData] = useState({
        "title": "",
        "slug": "",
        "body": "",
        "tags": "",
        "categories": [],
        "img": "",
    })

    const handelInputChange = (event) => {
        const { name, value } = event.target;
        setBlogData((blogData) => ({
            ...blogData,
            [name]: value
        }));
    };
    const handleFileChange = (event) => {
        setBlogData((blogData) => ({
            ...blogData,
            ['img']: event.target.files[0] // Match the key used in FormData
        }));
    };


    const handleSubmit = (e) => {
        e.preventDefault();
        const form = {
            'title': blogData.title,
            'slug': blogData.slug,
            'body': blogData.body,
            'tags': blogData.tags,
            'img': blogData.img,
            'categories': blogData.categories,
            'status': document.getElementById('status2').checked

        }
        console.log(form);
        api.post(baseurl + '/author/posts/', form, {
            headers: { "Content-Type": "multipart/form-data" },
        })
            .then(response => {
                console.log('Post created:', response.data);
            })
            .catch(error => {
                console.error('Error creating post:', error);
            });

        setBlogData({

            "title": "",
            "slug": "",
            "body": "",
            "tags": "",
            "categories": "",
            "img": "",
        })
    };

    useEffect(() => {
        api.get(baseurl + '/categories/') // Adjust the URL to your actual category endpoint
            .then(response => {
                setCategoriesData(response.data);
            })
            .catch(error => {
                console.error('Error fetching categories:', error);
            });
    }, []);



    return (
        <div className='grid grid-cols-3 bg-gray-300'>
            <div className='col-span-1'>
                <Dashboard />
            </div>
            <div className='col-span-2 mr-24 mt-16'>
                <div className="lg:ms-auto mx-auto text-center">
                    <div className="py-16 px-7 rounded-md bg-white">
                        <form action="" onSubmit={handleSubmit}>
                            <div className="grid md:grid-cols-2 grid-cols-1 gap-6">
                                <input
                                    type="text"
                                    id="fname"
                                    name="title"
                                    placeholder="Title"
                                    value={blogData.title}
                                    onChange={handelInputChange}
                                    className="w-full border border-gray-300 rounded-md py-2 px-3 focus:outline-none focus:border-blue-700"
                                />
                                <input
                                    type="text"
                                    id="fname"
                                    name="slug"
                                    placeholder="Slug"
                                    value={blogData.slug}
                                    onChange={handelInputChange}
                                    className="w-full border border-gray-300 rounded-md py-2 px-3 focus:outline-none focus:border-blue-700"
                                />

                                <div className="md:col-span-2">
                                    <label htmlFor="categories" className="float-left block font-normal text-gray-400 text-lg">
                                        Categories:
                                    </label>
                                    <select
                                        id="subject"
                                        name="categories"
                                        
                                        onChange={handelInputChange}
                                        className="w-full border border-gray-300 rounded-md py-2 px-3 focus:outline-none focus:border-blue-700"
                                    >
                                        <option value="" disabled selected>
                                            Choose category:
                                        </option>
                                        {categoriesData?.map((category) => {
                                            return (

                                                <option key={category.id} value={category.id}>
                                                    {category.name}
                                                </option>

                                            )
                                        })}
                                    </select>
                                </div>


                                <div className="md:col-span-2">
                                    <label htmlFor="file" className="float-left block font-normal text-gray-400 text-lg">
                                        Image:
                                    </label>
                                    <input
                                        type="file"
                                        name='img' // Match the key in FormData
                                        onChange={handleFileChange}
                                        id="ProfileImage"
                                        aria-describedby="emailHelp"
                                        className="peer block w-full appearance-none border-none bg-transparent py-2.5 px-0 text-sm text-gray-900 focus:border-blue-600 focus:outline-none focus:ring-0"
                                    />

                                </div>
                                <div className="md:col-span-2">
                                    <label htmlFor="status2" className="float-left block font-normal text-gray-400 text-lg">
                                        Status:
                                    </label>
                                    <input
                                        type="checkbox"
                                        id="status2"
                                        name="file"
                                        className='transform scale-150'
                                    />
                                </div>


                                <div className="md:col-span-2">
                                    <label htmlFor="subject" className="float-left block font-normal text-gray-400 text-lg">
                                        Body:
                                    </label>
                                    <textarea
                                        name="body"
                                        rows="5"
                                        cols=""
                                        placeholder="Body..."
                                        value={blogData.body}
                                        onChange={handelInputChange}
                                        className="w-full border border-gray-300 rounded-md py-2 px-3 focus:outline-none focus:border-blue-700"
                                    ></textarea>
                                </div>
                                <div className="md:col-span-2">
                                    <textarea
                                        name="tags"
                                        rows="5"
                                        cols=""
                                        placeholder="Tags, please separate with ,"
                                        value={blogData.tags}
                                        onChange={handelInputChange}
                                        className="w-full border border-gray-300 rounded-md py-2 px-3 focus:outline-none focus:border-blue-700"
                                    ></textarea>
                                </div>
                                <div className="md:col-span-2">
                                    <button className="py-3 text-base font-medium rounded text-white bg-blue-800 w-full hover:bg-blue-700 transition duration-300">
                                        Valider
                                    </button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default AuthorCreateBlogs;

I can choose one category and send it to date base and create a post successfly but I want that user be able choose multyple item...

Upvotes: 0

Views: 58

Answers (1)

i-wizard
i-wizard

Reputation: 314

There are multiple ways to do this;

  1. You could use a library like Antdesign that comes with some really nice dropdown feature with multiselect option. Here is a link to ant design's multiselect input [https://ant.design/components/select][1] and here is what the multi-select box looks like [https://ant.design/~demos/select-demo-big-data][2]. Here is the Javascript code snippet for the above multi-select input.
import React from 'react';
import { Select, Typography } from 'antd';
const { Title } = Typography;
const options = [];
for (let i = 0; i < 100000; i++) {
  const value = `${i.toString(36)}${i}`;
  options.push({
    label: value,
    value,
    disabled: i === 10,
  });
}
const handleChange = (value) => {
  console.log(`selected ${value}`);
};
const App = () => (
  <>
    <Title level={4}>{options.length} Items</Title>
    <Select
      mode="multiple"
      style={{
        width: '100%',
      }}
      placeholder="Please select"
      defaultValue={['a10', 'c12']}
      onChange={handleChange}
      options={options}
    />
  </>
);
export default App;
  1. You can also add the multiple attribute to your select tag like this
    <select
        id="subject"
        name="categories"
        onChange={handelInputChange}
        className="w-full border border-gray-300 rounded-md py-2 px-3 focus:outline-none focus:border-blue-700"
        multiple // Added the multiple attribute here
    >

but i think your handelInputChange function on the select input will not allow accepting multiple options because your function setBlogData here

setBlogData((blogData) => ({
            ...blogData,
            [name]: value
        }));

is replacing the the old value with the a new one. Maybe create a new handler for the select input

const handelSelectInputChange = (event) => {
        const { name, value } = event.target;
    setBlogData((blogData) => ({
                ...blogData,
                categories: value + old_value
            }));
        
    };

So the each new select does not replace the old one.

Upvotes: 0

Related Questions