Загрузить через FormData массив объектов
Пишу функцию создания товара . Мне нужно ,чтобы с помощью FormData при создании товара , товар был создан со списком пользователей . С одним пользователем мной уже было реализовано . Много чего мной было перепробовано для создания множественного выбора : 1) React-select, 2) Prime-React, 3) Стандартный HTML выбор (с атрибутом multiple) 4), React-multi-select-component , 5) multiselect-React-dropdown. Выдаёт одну и ту же ошибку : Product validation failed: partners.0: Cast to [ObjectId] failed for value "["6"]" (type string) at path "partners.0" . Прошу помочь и объяснить как с помощью FormData загрузить массив (в данном случае пользователей)
Форма создания товара:
import React, { Fragment, useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { clearErrors, createProduct } from "../../actions/productAction";
import {loadAllUsers , clearCrashes} from "../../actions/userAction";
import { useAlert } from "react-alert";
import { Button } from "@material-ui/core";
import MetaData from "../layout/MetaData";
import AccountTreeIcon from "@material-ui/icons/AccountTree";
import DescriptionIcon from "@material-ui/icons/Description";
import StorageIcon from "@material-ui/icons/Storage";
import SpellcheckIcon from "@material-ui/icons/Spellcheck";
import AttachMoneyIcon from "@material-ui/icons/AttachMoney";
import SideBar from "./Sidebar";
import { NEW_PRODUCT_RESET } from "../../constants/productConstants";
const NewProduct = ({ history }) => {
const dispatch = useDispatch();
const alert = useAlert();
const { loading, error, success } = useSelector((state) => state.newProduct);
const { crash , users } = useSelector((state) => state.allUsersLoad);
const [title, setTitle] = useState("");
const [price, setPrice] = useState(0);
const [description, setDescription] = useState("");
const [category, setCategory] = useState("");
const [Stock, setStock] = useState(0);
const [color , setcolor] = useState("");
const [user , setuser] = useState("");
const [partners , setpartners] = useState([])
const [images, setImages] = useState([]);
const [imagesPreview, setImagesPreview] = useState([]);
const categories = [
"Laptop",
"Footwear",
"Bottom",
"Tops",
"Attire",
"Camera",
"SmartPhones",
];
const colors = ["Black" ,"Green","Grey","Red","Yellow","Brown" ,"White"];
useEffect(() => {
if (crash) {
alert.error(crash);
dispatch(clearCrashes());
}
dispatch(loadAllUsers());
}, [dispatch, alert, crash]);
useEffect(() => {
if (error) {
alert.error(error);
dispatch(clearErrors());
}
if (success) {
alert.success("Product Created Successfully");
history.push("/admin/dashboard");
dispatch({ type: NEW_PRODUCT_RESET });
}
}, [dispatch, alert, error, history, success]);
const createProductSubmitHandler = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("title", title);
myForm.set("price", price);
myForm.set("description", description);
myForm.set("category", category);
myForm.set("Stock", Stock);
myForm.set("color", color);
myForm.set("user", user);
//partners.forEach((obj) =>{
// Object.entries(obj).forEach((item , i) =>{
// myForm.append("partners",`[${i}][${item[0]}]`); //??
// })
// })
Object.values(partners).forEach(partner=>{
myForm.append("partners", partner); //??
});
images.forEach((image) => {
myForm.append("images", image);
});
dispatch(createProduct(myForm));
};
const createProductImagesChange = (e) => {
const files = Array.from(e.target.files);
setImages([]);
setImagesPreview([]);
files.forEach((file) => {
const reader = new FileReader();
reader.onload = () => {
if (reader.readyState === 2) {
setImagesPreview((old) => [...old, reader.result]);
setImages((old) => [...old, reader.result]);
}
};
reader.readAsDataURL(file);
});
};
return (
<Fragment>
<MetaData title="Create Product" />
<div className="dashboard">
<SideBar />
<div className="newProductContainer">
<form
className="createProductForm"
encType="multipart/form-data"
onSubmit={createProductSubmitHandler}
>
<h1>Create Product</h1>
<div>
<SpellcheckIcon />
<input
type="text"
placeholder="Product Title"
required
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div>
<AttachMoneyIcon />
<input
type="number"
placeholder="Price"
required
onChange={(e) => setPrice(e.target.value)}
/>
</div>
<div>
<DescriptionIcon />
<textarea
placeholder="Product Description"
value={description}
onChange={(e) => setDescription(e.target.value)}
cols="30"
rows="1"
></textarea>
</div>
<div>
<AccountTreeIcon />
<select onChange={(e) => setCategory(e.target.value)}>
<option value="">Choose Category</option>
{categories.map((cate) => (
<option key={cate} value={cate}>
{cate}
</option>
))}
</select>
</div>
<div>
<StorageIcon />
<input
type="number"
placeholder="Stock"
required
onChange={(e) => setStock(e.target.value)}
/>
</div>
<div>
<AccountTreeIcon />
<select onChange={(e) => setcolor(e.target.value)}>
<option value="">Choose Color</option>
{colors.map((co) => (
<option key={co} value={co}>
{co}
</option>
))}
</select>
</div>
<div>
<AccountTreeIcon />
<select onChange={(e) => setuser(e.target.value)}>
<option value="">Choose User</option>
{users.map((us) => (
<option key={us._id} value={us._id}>
{us.name}
</option>
))}
</select>
</div>
<div>
<AccountTreeIcon />
<select multiple onChange={(e) => {setpartners(e.target.value[0])}}>
<option value="">Choose Partners</option> //??
{users.map((us) => (
<option key={us._id} value={us._id}>
{us.name}
</option>
))}
</select>
</div>
<div id="createProductFormFile">
<input
type="file"
name="avatar"
accept="image/*"
onChange={createProductImagesChange}
multiple
/>
</div>
<div id="createProductFormImage">
{imagesPreview.map((image, index) => (
<img key={index} src={image} alt="Product Preview" />
))}
</div>
<Button
id="createProductBtn"
type="submit"
disabled={loading ? true : false}
>
Create
</Button>
</form>
</div>
</div>
</Fragment>
);
};
export default NewProduct;
Модель товара:
сonst mongoose = require("mongoose");
const productSchema = mongoose.Schema({
title: {
type: String,
required: [true, "Please Enter product Title"],
trim: true,
},
description: {
type: String,
required: [true, "Please Enter product Description"],
},
price: {
type: Number,
required: [true, "Please Enter product Price"],
maxLength: [8, "Price cannot exceed 8 characters"],
},
user: {
type: mongoose.Schema.ObjectId,
ref: "User",
required: true,
},
partners: [
{
type: mongoose.Schema.ObjectId,
ref: "User",
required: true,
}
],
ratings: {
type: Number,
default: 0,
},
images: [
{
public_id: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
},
],
category: {
type: String,
required: [true, "Please Enter Product Category"],
},
Stock: {
type: Number,
required: [true, "Please Enter product Stock"],
maxLength: [4, "Stock cannot exceed 4 characters"],
default: 1,
},
color: {
type: String,
default: "Not set!",
enum: ["Black", "Green", "Grey", "Red", "Yellow","Brown","White"],
},
sold: {
type: Number,
trim: true,
default: 0,
},
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model("Product", productSchema);
Ответы (1 шт):
Возможно , это кому либо поможет в будущем:
Добавить в импорт
import Select from "react-dropdown-select"Изменить Choose Partners на:
<h6>Select Partners</h6> <Select name="Partners Select" options={users} labelField="id" valueField="name" multi onChange={partners =>setpartners(partners)} color="green" > </Select> {partners.map((pa)=>( <p>Name :{pa.name}</p> ))} </div>В FormData
Object.values(partners).forEach(partner=>{ myForm.append("partners", partner); //?? }); изменить на partners.forEach(partner =>{myForm.append("partners",partner._id)})
Спасибо всем отозвавшимся. Всё заработало ,вопрос закрыт!