How to append multiple files to formData? - javascript

I have a form, and I have an input in the form, I upload several images in this input, like so:
<input type="file" name="gallery" onChange={imageHandler} required multiple />
And when I output the images in the console, it correctly displays an array of files, but when I append the images, formData.append("gallery", images); it is not saved as an array. It's saved as a File!
const [selectedFile, setSelectedFile] = useState();
const [images, setImages] = useState([]);
const token = localStorage.getItem("TOKEN");
const imageHandler = (e) => {
setImages([...images, e.target.files[0]]);
};
useEffect(() => {
images.length && console.log(images);
}, [images]);
console.log(images);
const sendDisease = async (e) => {
e.preventDefault();
console.log(images);
const form = document.getElementById("signUpForm");
const formData = new FormData(form);
formData.append("gallery", images);
console.log(formData);
try {
const api = "https://localhost/api/v1/website/disease/add";
const { data } = await axios.post(api, formData, {
headers: {
headers: { "Content-Type": "multipart/form-data" },
Authorization: `Bearer ${token}`,
},
});
toast.success(data.message, { autoClose: 15000 });
} catch (e) {
console.log(e);
toast.error(e.response.data.error, { autoClose: 15000 });
console.log(e.response.data.error);
}
};
const handleFileSelect = (event) => {
setSelectedFile(event.target.files[0]);
};
return (
<>
<div className="container">
<div className="row">
<form id="signUpForm" className="md-12">
<h2 className="text-center mb-4 font-weight-bold">Add Disease</h2>
<div className="form-row mt-4">
<div className="col-md-6 mb-3">
<label for="">Add Disease</label>
<input
type="text"
className="form-control"
name="unique_name"
required
onChange={(e) => setUniqueName(e.target.value)}
/>
</div>
<div className="col-md-6 mb-3">
<label for="">Title</label>
<input
type="text"
className="form-control"
name="title"
placeholder="Title"
onChange={(e) => setTitle(e.target.value)}
required
/>
</div>
</div>
<div className="form-row">
<div className="col-md-12 mb-3">
<label for="">Gallery</label>
<input type="file" name="gallery" onChange={imageHandler} required multiple />
</div>
</div>
<input type="file" name="image" onChange={handleFileSelect} />
</div>
<button
type="button"
className="btn btn-primary mt-4"
onClick={sendDisease}
>
Submit
</button>
</form>
</div>
</div>
</>
);

Calling new FormData(form) is enough. You don't need to call formData.append("gallery", images) as the constructor will pick on its own all the images for you. So you may not even need this images state.
The code below would log all your files if you wanna be sure about it. I also created a working Codesandbox for testing.
export default function App() {
const handleSubmit = (e) => {
e.preventDefault();
const form = document.getElementById("signUpForm");
const formData = new FormData(form);
console.log(formData.getAll("gallery"));
};
return (
<form id="signUpForm" onSubmit={handleSubmit}>
<input type="file" name="gallery" required multiple />
<button>Sumbit</button>
</form>
);
}
If it's not working, try changing formData.append("gallery", images) to the code below (it's apparently what's needed for PHP servers for example):
images.forEach((image) => {
formData.append('gallery[]', image);
});

Here in my case Am using ASP.NET Core and its working fine
images.forEach((image) => {
formData.append('images', image);
});
My Controller
[HttpPost]
public async Task<IActionResult> UpdateProfile([FromForm] UpdateProfileModel updateProfileModel)
{
try
{
await Task.CompletedTask;
return Ok("Done.");
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
My Modal
public class UpdateProfileModel
{
public List<IFormFile> Images { get; set; } = new List<IFormFile>();
}

Related

How do I add user inputs on click?

Self taught coder. Hopefully I can explain this adequately. Users create recipe cooking guides by inputing values like dish name, food category, cover img, etc. The steps array only has 1 step at the moment. I want a button that users can click that would add another "step" and "gif" to the steps array. Can anyone suggest a method to have another set of "step" and "img" render on screen when the user clicks the Add Step button? I have a handleAddSteps function with nothing inside. Somebody recommended using DOM? Just want to know if this is a good way to go before investing time down the wrong rabbit hole. Thank you kindly for any advice.
import React from "react";
import { useState, useEffect } from "react";
// Styles
import "./UploadGuide.scss";
// Firebase
import { doc, setDoc } from "firebase/firestore";
import { db, storage } from "../../firebase";
import { v4 as uuidv4 } from "uuid";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
function UploadGuide() {
const [file, setFile] = useState("");
const [data, setData] = useState({});
const [percentage, setPercentage] = useState(null);
// const [stepsCounter, setStepsCounter] = useState(0);
// Upload IMG
useEffect(() => {
const uploadFile = () => {
const name = new Date().getTime() + file.name;
const storageRef = ref(storage, file.name);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
setPercentage(progress);
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
default:
break;
}
},
(error) => {
console.log(error);
},
() => {
// Handle successful uploads on complete
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setData((prev) => ({ ...prev, img: downloadURL }));
});
}
);
};
file && uploadFile();
}, [file]);
console.log(file);
console.log(data);
// When user clicks Add Steps button, page renders another steps card
const handleAddSteps = () => {};
// When user submits guide, upload data to firecloud db and imgs to firebase storage.
const handleAddGuide = async (e) => {
e.preventDefault();
const newId = uuidv4();
// const stepsArray = []
await setDoc(doc(db, "guides", newId), {
author: e.target.author.value,
categoryId: "100",
categoryName: e.target.categoryName.value,
coverGif: data.img,
id: newId,
name: e.target.name.value,
tags: e.target.tags.value,
// steps: stepsArray
steps: [
{
step: e.target.step.value,
gif: data.img,
},
],
});
};
return (
<div className="upload">
<form onSubmit={handleAddGuide} className="upload__form">
<div className="upload__card">
<div className="upload__inputs">
<label className="upload__label">Guide Name</label>
<input
className="upload__input"
placeholder="Name of your recipe"
type="name"
name="name"
// value=""
></input>
</div>
<div className="upload__inputs">
<label className="upload__label">Author</label>
<input
className="upload__input"
type="author"
name="author"
></input>
</div>
<div className="upload__inputs">
<label className="upload__label">Category</label>
<select
name="categoryName"
className="upload__input"
defaultValue={"Cooking"}
>
<option value="Cooking">Cooking</option>
<option></option>
</select>
</div>
<div className="upload__inputs">
<label className="upload__label">Tags</label>
<input
name="tags"
className="upload__input upload__input--large"
placeholder="Add relevant tags to help people find your guide"
></input>
</div>
</div>
<div className="upload__card upload__card--addstep">
<div className="upload__inputs">
<label className="upload__label">Step 1</label>
<input
className="upload__input upload__input--large"
name="step"
></input>
</div>
<div className="upload__inputs">
<div>
<img src={file ? URL.createObjectURL(file) : ""}></img>
</div>
<label
htmlFor="file"
style={{ cursor: "pointer" }}
className="upload__add"
>
+ IMG
</label>
<input
id="file"
onChange={(e) => setFile(e.target.files[0])}
style={{ display: "none" }}
type="file"
></input>
</div>
</div>
<div className="upload__box">
<button
className="upload__add"
onClick={handleAddSteps}
style={{ cursor: "pointer" }}
>
Add Step
</button>
</div>
<div className="upload__cta">
<button
disabled={percentage !== null && percentage < 100}
type="submit"
className="upload__submit"
style={{ cursor: "pointer" }}
>
Upload
</button>
</div>
</form>
</div>
);
}
export default UploadGuide;
Here is an image of what the page looks like:
enter image description here
Please let me know if I'm missing any key information. Any advice is appreciated. Thank you.
Since each Guide can have multiple steps, I would rather create a state that handles an array of multiple step objects, than just a counter.
With this, you can render as much steps as you have in the array.
Then, adding steps just will be a matter of adding objects (with an empty state) to that array.
I made this functional example, that should be clear enough to show what I mean:
import React, { useState } from 'react';
const EMPTY_STEP = { text: '', file: null };
export default function App() {
const [guideSteps, setGuideSteps] = useState([{ ...EMPTY_STEP }]);
const handleStepChange = (stepIndex, property, value) => {
setGuideSteps((prevState) => {
const newGuideSteps = [...prevState];
newGuideSteps[stepIndex] = { ...newGuideSteps[stepIndex], [property]: value };
return newGuideSteps;
});
};
const handleAddStep = () => {
setGuideSteps((prevState) => [...prevState, { ...EMPTY_STEP }]);
};
return (
<div className="Steps">
{guideSteps.map((step, index) => {
return (
<div key={`step-${index + 1}`}>
<h2>{`Step ${index + 1}`}</h2>
<input type="text" value={step.text} onChange={(e) => handleStepChange(index, 'text', e.target.files ? e.target.files[0] : null, index)} />
<input type="file" value={step.file} onChange={(e) => handleStepChange(index, 'file', e.target.value, index)} />
</div>
);
})}
<button onClick={handleAddStep}>Add Step</button>
</div>
);
}

How to post data to supabase through next.js

I currently have a simple next.js website where users can look at projects for an organization, and at the bottom of the page, the user can input a new project through the use of a form with multiple inputs. The database that i am currently using is Supabase.
My code currently takes in user input from each input box and stores them inside the newProject const, after which the data is then parsed into the createNewProject function and sent to Supabase.
const initialState = { solution_id: '', organization_id: '', budget_usd: '',other_info: '',country: '',project_duration_days: '',status: '',date_time_timezone: '' }
export default function Projects({ Projects }) {
useEffect(() => {
console.log(Projects)
}, [])
const [newProject, setProject] = useState(initialState)
console.log("User inputed data")
console.log(newProject)
const {solution_id, organization_id, budget_usd, other_info, country,project_duration_days,status,date_time_timezone} = newProject
const router = useRouter()
function onChange(e) {
setProject(() => ({ ...newProject, [e.target.name]: e.target.value }))
}
async function createNewProject() {
if (!solution_id || !organization_id || !country) return
const id = uuid()
newProject.id = id
const {data} = await supabase
.from('Projects')
.insert([
{ solution_id, organization_id, budget_usd,other_info,country,project_duration_days,status,date_time_timezone }
])
router.push(`/projects/${data.id}`)
}
return (
<div>
{Projects.map(project => {
return (
<div key={project.id}>
<h1><b>{project.Solutions.name} in {project.country}</b></h1>
<h2>Budget USD: {project.budget_usd}</h2>
<h2>Duration: {project.project_duration_days} days</h2>
<h2>Status: {project.status}</h2>
<h2>Organization: {project.Organizations.name}</h2>
<h2>Project Id: {project.id}</h2>
<button type="button" onClick={() => router.push(`/projects/${project.id}`)}>Donate now</button>
<br></br>
</div>
)
})}
<label htmlFor="solution_id ">solution_id</label>
<input onChange={onChange} value={newProject.solution_id} type="text" id="solution_id" name="solution_id" required />
<label htmlFor="organization_id ">organization_id</label>
<input onChange={onChange} value={newProject.organization_id} type="text" id="organization_id" name="organization_id" required />
<label htmlFor="budget_usd ">Last budget_usd</label>
<input onChange={onChange} value={newProject.budget_usd} type="text" id="budget_usd" name="budget_usd" required />
<label htmlFor="other_info ">other_info</label>
<input onChange={onChange} value={newProject.other_info} type="text" id="other_info" name="other_info" required />
<label htmlFor="country ">country</label>
<input onChange={onChange} value={newProject.country} type="text" id="country" name="country" required />
<label htmlFor="project_duration_days ">Project Duration Days</label>
<input onChange={onChange} value={newProject.project_duration_days} type="text" id="project_duration_days" name="project_duration_days" required />
<label htmlFor="status ">status</label>
<input onChange={onChange} value={newProject.status} type="text" id="status" name="status" required />
<label htmlFor="date_time_timezone ">date_time_timezone</label>
<input onChange={onChange} value={newProject.date_time_timezone} type="text" id="date_time_timezone" name="date_time_timezone" required />
<button type="button" onClick={createNewProject} >Submit new project</button>
</div>
)
}
export async function getServerSideProps() {
const fetchOrgs = async () => {
let { data: Organizations, error } = await supabase
.from('Organizations')
.select('*')
return Organizations
}
const fetchSolutions = async () => {
let { data: Solutions, error } = await supabase
.from('Solutions')
.select('*')
return Solutions
}
const fetchProjects = async () => {
let { data: Projects, error } = await supabase
.from('Projects')
.select(`
id,
solution_id,
organization_id,
budget_usd,
country,
project_duration_days,
status,
Solutions(name),
Organizations(name)
`)
.order('id', { ascending: true })
console.log(Projects)
return Projects
}
const Organizations = await fetchOrgs();
const Solutions = await fetchSolutions();
const Projects = await fetchProjects();
return { props: { Solutions, Organizations, Projects } }
}
However, whenever I press the submit button, the console.log for the newProject, would show that there is not data being passed into the variables, only the empty placeholder data in the initialState const. As such, I am unsure about how to pass data from next.js input forms into a variable to be posted into supabase.

i have a problem to change the format of date when i am passing to mysql db using react.js

Hello guys, I set my entered date to dd/mm/yyy format using moment(donors.donateDate).format('DD-MM-YYYY')
But when click on submit button it shows this error in console.
The specified value "23-03-2022" does not conform to the required format, "yyyy-MM-dd".
this is myCode:-
import { TextField } from '#material-ui/core';
import moment from 'moment';
export default function DonorForm ({showForm}) {
const [donors, setDonors] = useState({
donateDate: ""
});
let name, value;
const handleInputs = (e) => {
console.log(e);
name = e.target.name;
value = e.target.value;
setDonors({...donors, [name]: value });
};
// For changing date format in TextFeild.
const newDate1 = moment(donors.donateDate).format('DD-MM-YYYY')
console.log(newDate1)
const onclick = async (e) => {
const {donateDate } =
donors;
const config = {
header: {
"Content type": "appication/json",
},
};
setLoading(true);
const { data } = await axios.post(
"http://localhost:4000/donorsform",
{
donateDate
},
config
);
console.log(data);
localStorage.setItem("donorsInfo", JSON.stringify(data));
navigate("/coordinators/myprojects");
};
return(
<>
<div className="card-header ">
<h3 className="card-title my-1">Donor Details</h3>
</div>
<form
onSubmit={handleSubmit(onclick)}
method="POST"
className="form-horizontal "
id="register-form"
>
<div className="card-body">
<div className="row">
<div className="form-group">
<label className="form-label col">Date</label>
<div>
<TextField
onChange={handleInputs}
id="donateDate"
type="date"
className="form-control col-12 mx-1"
name="donateDate"
value={newDate1}
/>
</div>
</div>
</div>
</div>
<div className="card-footer">
<button type="submit" className="btn-hover-1 color-3">
Submit
</button>
</div>
</form>
</>
);
};
And in the end, I also want to do if user visit this form the 'donateDate' field shows current date by default.
your backend expecting date in the form YYYY-MM-DD
So you can do like this as shown below
const newDate1 = moment(donors.donateDate).format('YYYY-MM-DD')

how to render data from for loop?

I want to render data from API which I render it through for loop but when I'm displaying it to UI, it displays only 1 value.
Specification: - when user upload a text image, API displays text which is present in the image and name, which is the JSON data is an array so I render DetectedText value-form API using for loop but not able to display all value of DetectedText in UI so please tell me where I'm doing wrong please see the below code
import React, { Component } from 'react';
import axios from 'axios'
class Image extends Component {
constructor(props) {
super(props);
this.state = { file: '', imagePreviewUrl: '', selectedOption: '', change: [], response: [], path: [], jsonData: [], dText: [] };
}
handleSubmit(e) {
e.preventDefault();
var byteCode = this.state.imagePreviewUrl.substring((e.target.value).indexOf(',') + 23);
let url = "http://192.168.4.138/MediaAnalysisImage_Amazon/api/DetectText/DetectText";
const data = { "fileByte": byteCode }
const response = axios.post(url, data)
.then(response => {
this.setState({
change: response,
byteArr: response.data.fileByte,
jsonData: response.data.jsondata.replace(/[&\/\\#+()$"~%.'*?<>{}]/g, ''),
path: response.data.jsondata.DetectedText,
});
console.log('json detected data', this.state.jsonData)
const parseData = JSON.parse(response.data.jsondata)
let x = ""
for (let x of parseData) {
const DetectedText = x.DetectedText
this.setState({
dText: DetectedText
})
console.log('setting dtext', this.state.dText)
}
})
}
handleImageChange(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
file: file,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file)
}
render() {
const img = "data:image/png;base64" + ',' + this.state.byteArr
let { imagePreviewUrl } = this.state;
let $imagePreview = null;
if (imagePreviewUrl) {
$imagePreview = (<img src={imagePreviewUrl} className="img-responsive imgp" />);
} else {
$imagePreview = (<span className="previewText">Please select an Image for Preview</span>);
}
return (
<div className="wrapper">
<h2 className="text-center heading" >Text Recognization</h2>
<div className="container ">
<section className="celeb">
<form className="Fform bg-light mb-4">
<div class="form-group ">
<input className="fileInput"
type="file"
onChange={(e) => this.handleImageChange(e)} class="btn btn-secondary" />
</div>
<button className="btn btn-success"
type="submit"
onClick={(e) => this.handleSubmit(e)}>Upload Image</button>
</form>
<hr></hr>
<div className="row grid">
<div className="col-md-6">
<h3>Input Image</h3>
{$imagePreview}
</div>
<div className="col-md-6">
<h3>Output Image</h3>
<img src={img} className="img-responsive imgp" />
</div>
</div>
<div>
<hr></hr>
<h4>Description </h4>
<table className="table table-hover">
<tr>
<th ><label>Name :- </label></th>
<td>{this.state.dText}</td>
</tr>
</table>
</div>
</section>
</div>
</div>
)
}
}
export default Image;
You may change like this:
this.setState({
dText: parseData.map(x => x.DetectedText),
})
and in your render method:
<td>{ this.state.dText.map((text, index) => <div key={index}>{text}</div>) }</td>

Ajax call issue

I'm sending an image attribute file by ajax call. But the post api might be constantly breaking for this. I want to push the file in the image attribute array in the formData & have the successful api call.
My Api form data configuration is as shown in the following image:
When I'm checking the formData image attribute array on Console I'm getting the thing image attributes[]: [object file] as shown in the following image:
& my api is constantly failing and showing the following error:
Following is the code I'm currently stuck on:
/*NewPost.jsx */
handleSubmit = (event) => {
event.preventDefault();
console.log("Submitting")
let error = this.areThereAnyErrors();
if(!error){
let data = {};
data.description = this.state.description.value || "";
// console.log(this.state.images_attributes);
data.images_attributes = this.state.images_attributes[0] || null;
let brand_string = this.state.brand_ids[0].value;
let brands_list = this.props.auto_complete_details.brand_json;
let selected_brand = brands_list.find(function (brand) {
return brand.brand_name.toLowerCase() == brand_string.toLowerCase();
});
if(selected_brand){
this.state.brand_selected.value = selected_brand.brand_id;
}
if(this.state.brand_selected.value){
data.Brand = this.state.brand_selected.value;
} else {
// console.log(this.state.brand_ids);
data.custom_brand = this.state.brand_ids[0].value;
}
let product_string = this.state.product_ids[0].value;
let product_list = this.props.auto_complete_details.product_json;
let selected_product = product_list.find(function (product) {
return product.product_name.toLowerCase() == product_string.toLowerCase();
});
if(selected_product){
this.state.product_selected.value = selected_product.product_id;
}
if(this.state.product_selected.value){
data.Product = this.state.product_selected.value;
} else {
data.custom_product = this.state.product_ids[0].value;
}
data.Price = this.state.price.value;
this.props.onFormSubmit(data);
}
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<div className={"row description new_post_desc "+error_class}>
<div className="col-md-12 col-xs-12" style={{"paddingRight":"0px"}}>
<textarea id="new_post_desc" name="description" className='new-post-description' placeholder="Share your brand story..." ref={(input) => {this.state.description = input}}/>
</div>
</div>
<div className="description">
<div className="row">
<input id="new_post_image" className={this.state.errors.images_attributes ? "error-input disp_inline": "disp_inline"} type="file" name="image" accept="image/*" ref={(input) => {this.state.images_attributes[0] = input}} />
</div>
<div className="row margin-top-10">
<div className="col-md-2 col-sm-2 col-xs-3 new_post_spacing">
<input id='autocomplete_brand' className={this.state.errors.brand_ids ? 'typeahead error-input new_post_capsules tt-hint hint_user_info' : 'typeahead new_post_capsules hint_user_info'} type="text" name="brand" placeholder="Brand" ref={(input) => {this.state.brand_ids[0] = input}}/>
<input id="brand_value_selected" style={{display: "none"}} value="" ref={(input) => {this.state.brand_selected = input}}/>
</div>
<div className="col-md-2 col-sm-2 col-xs-3 new_post_spacing">
<input id='autocomplete_product' className={this.state.errors.product_ids ? 'typeahead error-input new_post_capsules tt-hint hint_user_info' : 'typeahead new_post_capsules hint_user_info'} type="text" name="product" placeholder="Product" ref={(input) => {this.state.product_ids[0] = input}}/>
<input id="product_value_selected" style={{display: "none"}} value="" ref={(input) => {this.state.product_selected = input}} />
</div>
<div className="col-md-2 col-sm-2 col-xs-3 new_post_spacing">
<input id='autocomplete_price' className={this.state.errors.price ? 'typeahead new_post_capsules tt-hint hint_user_info error-input' : 'typeahead new_post_capsules tt-hint hint_user_info'} type="number" name="price" placeholder="Price" ref={(input) => {this.state.price = input}}/>
</div>
<div className="col-md-2 col-sm-2 col-xs-3 new_post_spacing">
<button type="submit" className="btn button-styling" style={{"outline":"none"}} disabled={this.props.new_post_creation_in_progress}>Share { loading_icon }</button>
</div>
</div>
</div>
<div className="row">
{ this.state.has_errors ? <div className="error_mesg">* Please provide required information</div> : '' }
</div>
</form>
);
}
const mapDispatchToProps = (dispatch, ownProps) => {
var that = this;
return {
dispatch,
onFormSubmit: (data) => {
dispatch(getNewPostDetails(data));
}
};
}
/* Actions/index.js */
export const getNewPostDetails = (data) => {
console.log(data);
var data_value = data;
return function(dispatch) {
var formData = new FormData();
// add assoc key values, this will be posts values
// setting form data
console.log(data_value);
//for image files
if(data_value.images_attributes) {
formData.append('images_attributes[]', $('input[type=file]')[0].files[0]);
}
//for video files
formData.append('video_attributes[]', null);
//for custom brands
if(data_value.custom_brand) {
formData.append('brand_ids[]', data_value.custom_brand);
}
//for products
if(data_value.custom_product) {
formData.append('product_ids[]', data_value.custom_product);
}
//For Price & Description
formData.append('price', data_value.Price);
formData.append('description', data_value.description);
for (let pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
$.ajax({
type: "POST",
url: `${mainurl}/api/v1/posts`,
// headers: checkAuthHeaders(), // the main solution
formData: formData,
beforeSend: function(){
$("#new_post_desc").prop('disabled', true);
$("#autocomplete_brand").prop('disabled', true);
$("#autocomplete_product").prop('disabled', true);
$("#autocomplete_price").prop('disabled', true);
dispatch(createPostProgress(true));
},
success: function(resp) {
$('#new_post_desc').val("");
$('#new_post_image').val("");
$('#autocomplete_brand').val("");
$('#autocomplete_product').val("");
$('#autocomplete_price').val("");
$("#brand_value_selected").val("");
$("#product_value_selected").val("");
$("#new_post_desc").prop('disabled', false);
$("#autocomplete_brand").prop('disabled', false);
$("#autocomplete_product").prop('disabled', false);
$("#autocomplete_price").prop('disabled', false);
dispatch(receiveNewPostDetails(resp));
dispatch(resetNewPostFlag());
dispatch(createPostProgress(false));
},
error: function(error) {
dispatch(createPostProgress(false));
},
async: true,
cache: false,
contentType: false,
processData: false,
timeout: 60000
});
};
}
[] At the end of the name of the attribute, it usually already means that this field is an array, and the value of this field should not be an array. So try to use:
formData.append('images_attributes[]', files[0]);
formData.append('images_attributes[]', files[1]);
formData.append('images_attributes[]', files[2]);
// etc..
Instead of
formData.append('images_attributes[]', files);
The same rule applies to other fields.

Categories