this is my productupdate page .whenn the user clicks on the update button the details preload here via viewmodel sent by action. the user can see its previously uploaded pictures and since i cant preload them via input pictures i use js function which it preloads and puts the string(location of picture) in an array called uploadediamges. you may ask why i preload images , the answer is so that admin can remove the prevoius pictures or change the main picture. everythings fine until i wanna post them to action (details of products,new uploaded picture via input,preloadediamges array which is(id,url,ismain)).i have to use formdata and not post data casue i have files, why i say this because when i use postdata in js i can actualy send and vind uploadedimages array to existingproductpictures in viewmodel but with post data i cant. i would appreciate the help
my function
<script>
let featureCounter = @(Model.Features?.Count() ?? 0);
let uploadedImages = []; // { id, url, file?, isMain }
const pictures = @Html.Raw(Json.Serialize(ViewBag.Pictures ?? new List<string>()));
</script>
<script src="~/js/product/porduct-form.js"></script>
<script>
function UpdateProduct() {
let data
= new FormData();
// Append basic product fields
data.append('Id', $("#Id").val());
data.append('Name', $("#Name").val());
data.append('Description', $("#Description").val());
data.append('HowToUse', $("#HowToUse").val());
data.append('Price', $("#Price").val());
data.append('Stock', $("#Stock").val());
data.append('BrandId', $("#BrandId").val());
data.append('CategoryId', $("#CategoryId").val());
data.append("ExistingProductPicturesJson", JSON.stringify(uploadedImages));
// Append dynamic features
$("[name^='Features']").each(function () {
data.append($(this).attr("name"), $(this).val());
});
// Append uploaded files (correct ID)
const productImages = document.getElementById("productImages");
if (productImages && productImages.files.length > 0) {
for (let i = 0; i < productImages.files.length; i++) {
data.append("Pictures", productImages.files[i]);
}
}
$.ajax({
url: '@Url.Action("UpdateProduct", "Product", new { area = "Admin" })',
type: "POST",
data: data,
processData: false,
contentType: false,
success: function (data) {
if (data.isSuccess) {
swal.fire('موفق!', data.message, 'success')
.then(() => location.reload());
} else {
swal.fire('هشدار!', data.message, 'warning');
}
},
error: function (request) {
alert(request.responseText);
}
});
}
</script>
my js file which fills the preloaded imagesa here
document.addEventListener('DOMContentLoaded', function () {
try {
preloadImagesFromServer();
} catch (error) {
console.error('Add product page error:', error);
}
});
// ---------- Preload Images ----------
function preloadImagesFromServer() {
const preview = document.getElementById('imagesPreview');
if (!preview) return;
uploadedImages = []; // reset before preloading
preview.innerHTML = '';
const mainPic = "@ViewBag.Main"; // optional main pic from server
pictures.forEach((url, index) => {
const isMain = url === mainPic || (!uploadedImages.some(img => img.isMain) && index === 0);
uploadedImages.push({ id: index, url: url, isMain: isMain });
const imageItem = document.createElement('div');
imageItem.className = 'image-preview-item';
imageItem.id = `image-${index}`;
imageItem.innerHTML = `
<img src="${url}" alt="تصویر ${index + 1}">
<button type="button" class="remove-btn" onclick="removeImage(${index})">×</button>
<button type="button" class="btn btn-sm" style="
position: absolute; bottom: 5px; left: 5px;
background: ${isMain ? '#8bc34a' : 'rgba(255,255,255,0.9)'};
color: ${isMain ? 'white' : '#666'};
border: none; border-radius: 8px;
padding: 3px 8px; font-size: 11px;"
onclick="setMainImage(${index})">
${isMain ? '✓ اصلی' : 'انتخاب اصلی'}
</button>
`;
preview.appendChild(imageItem);
});
// Ensure one image is main
if (!uploadedImages.some(img => img.isMain) && uploadedImages.length > 0) {
setMainImage(uploadedImages[0].id);
}
}
my viewmodel
namespace PetPaw.EndPoint.Areas.Admin.Models.ViewModels.Product
{
public class NewProductViewModel
{
public long Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
public string? HowToUse { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public long CategoryId { get; set; }
public long BrandId { get; set; }
public long AnimalId { get; set; }
public bool IsActive { get; set; }
public int MainPictureIndex { get; set; }
public List<ExistingProductPicture> ExistingProductPictures { get; set; } = new List<ExistingProductPicture>();
public List<IFormFile> Pictures { get; set; }=new List<IFormFile>();
public List<FeatureViewModel> Features { get; set; }=new List<FeatureViewModel>();
}
public class ExistingProductPicture
{
public long id { get; set; }
public string url { get; set; }
public bool isMain { get; set; }
}
}
Request.Files