0

this is gonna be a long one so if you help me i would really appreciate it. i have a page where both create and update product is. my create page works find but i want this page to do update too so there are some preload data like features and pictures i want to be shown to admin which is fine i preloaded features and have no problem but i preloaded pictures with js(since i couldnt preload iformfille in viewmodel which yall will see) i cannt update my product cause i cant get those preloaded images sent to controller would really appreciate the HELP the below is my code My Action for getting update data from service

public IActionResult ProductDetail(NewProductViewModel product){

var brands = _brand.BrandLookupService().Excute().Select(b => new ProductsBrandViewModel { Id = b.Id, Name = b.Name });
var model = new NewProductViewModel();
var categories = _product.GetCategoriesService().Excute().Data.Select(CategoryMapper.ConvertDto);
if (product.Id==0)
{
    ViewBag.Brands = new SelectList(brands, "Id", "Name");
    ViewBag.Categories = new SelectList(CategoryMapper.FlattenCategoriesWithLevel(categories), "Id", "Name");
}
else
{
   var productDetail= _product.GetProductDetail().Excute(new CategoryDetailParamDTO { Id=product.Id});
    if (!productDetail.IsSuccess)
    {
        return NotFound();
    }
    model.Id = productDetail.Data.Id;
    model.Name=productDetail.Data.Name;
    model.Price=productDetail.Data.Price;
    model.Description=productDetail.Data.Description;
    model.HowToUse=productDetail.Data.HowToUse;
    model.IsActive = productDetail.Data.IsDisplayed;
    model.Stock = productDetail.Data.Quantity;
    model.Features = productDetail.Data.Features.Select(r => new FeatureViewModel { Id=r.Id,Name=r.DisplayName,Value=r.Value}).ToList();
    ViewBag.Main = productDetail.Data.MainPicture;
    ViewBag.Pictures= productDetail.Data.Pictures.OrderBy(p=>p==productDetail.Data.MainPicture);
    ViewBag.Brands = new SelectList(brands, "Id", "Name",productDetail.Data.BrandId);
    ViewBag.Categories = new SelectList(CategoryMapper.FlattenCategoriesWithLevel(categories), "Id", "Name",productDetail.Data.CategoryId);

}


return View(model);}

My cshtml Codes

@using PetPaw.EndPoint.Areas.Admin.Models.ViewModels.Product
@model NewProductViewModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

 *@{
    Layout = "~/Areas/Admin/Views/Shared/_AdminLayout.cshtml";

    if (Model.Id==0)
    {    ViewData["Title"] = "CreateProduct";
        
    }
    else
    
    {
        ViewData["Title"] = "UpdateProduct";

    }
 }
<div class="dashboard-header mb-4">
    <h2 style="color: #558b2f; font-size: 28px; font-weight: 700;">افزودن محصول جدید</h2>
    <p style="color: #999;">product info</p>
</div>

<form id="productForm" asp-area="Admin" asp-controller="Product" asp-action="CreateProduct" method="post" enctype="multipart/form-data">
    <div class="form-section">
        <h4>اطلاعات پایه</h4>
        <div class="row g-3">
            <div class="col-md-6">
                <label class="form-label">product name</label>
                <input asp-for="Name" type="text" class="form-control" required>
            </div>
            <div class="col-md-6">
                <label class="form-label">category</label>
                <select class="form-select" asp-items="ViewBag.Categories" >
                    <option>categories</option>
          
                </select>
            </div>
            <div class="col-md-6">
                <label class="form-label">brand</label>
                <select class="form-select" asp-items="ViewBag.Brands">
                    <option>brands</option>
                </select>
            </div>
            <div class="col-md-6">
                <label class="form-label">animal</label>
                <select class="form-select">
                    <option>dog</option>
                    <option>cat</option>
                    <option>all</option>
                </select>
            </div>
            <div class="col-12">
                <label class="form-label">Des</label>
                <textarea class="form-control" rows="3" asp-for="Description"></textarea>
            </div>
            <div class="col-12">
                <label class="form-label">How to use</label>
                <textarea class="form-control" rows="2" asp-for="HowToUse"></textarea>
            </div>
        </div>
    </div>

    <div class="form-section">
        <h4>stock and price</h4>
        <div class="row g-3">
            <div class="col-md-6">
                <label class="form-label">Price</label>
                <input type="number" class="form-control" required asp-for="Price">
            </div>
            <div class="col-md-6">
                <label class="form-label">stock</label>
                <input type="number" class="form-control" required asp-for="Stock">
            </div>
        </div>
    </div>

    <div class="form-section">
        <h4>features</h4>
        <div id="featuresContainer">
            <div id="featuresContainer">
                @if (Model.Id != 0)
                {
                    for (int i = 0; i < Model.Features.Count(); i++)
                    {
                        <div class="row g-2 mb-2" id="feature-@i">
                            <div class="col-md-4">
                                <input type="text" name="Features[@i].Name" value="@Model.Features[i].Name" class="form-control" placeholder="name" required />
                            </div>
                            <div class="col-md-4">
                                <input type="text" name="Features[@i].Value" value="@Model.Features[i].Value" class="form-control" placeholder="value" required />
                            </div>
                            <div class="col-md-4">
                                <button type="button" class="btn btn-sm" style="background: #ffebee; color: #c62828; border: none; border-radius: 10px; width: 100%;" onclick="removeFeature(@i)">
                                    <div class="icon-trash-2" style="display: inline-block; font-size: 14px;"></div>
                                    remove
                                </button>
                        </div>
                        </div>
                    }
                }
            </div>
      


        </div>
        <button type="button" class="btn-organic" style="background: #e3f2fd; color: #1976d2; margin-top: 10px;" onclick="addFeature()">
            <div class="icon-plus" style="display: inline-block; margin-left: 5px;"></div>
            add feature
        </button>
    </div>

    <div class="form-section">
        <h4>pics</h4>
        <div class="image-upload-area" onclick="document.getElementById('productImages').click()">
            <div class="icon-images text-2xl" style="color: #8bc34a;"></div>
            <p style="margin-top: 10px;">uplaod pic</p>
            <!-- File input sent by form -->
            <input asp-for="Pictures" type="file" id="productImages" accept="image/*" style="display: none;" onchange="handleImageUpload(event)" multiple>
            <input type="hidden" id="MainPictureIndex" name="MainPictureIndex" value="0">
        </div>
        <div id="imagesPreview" class="image-preview">

        </div>
        <p style="color: #999; font-size: 12px; margin-top: 10px;">first will be main bydefault or u can change it</p>
    </div>

    <div style="display: flex; gap: 10px; justify-content: flex-end;">
        <button type="button" class="btn-organic" style="background: #f5f5f5; color: #666;" onclick="window.location.href='products.html'">cancel</button>
        <button type="submit" class="btn-organic btn-primary-organic">save</button>
    </div>
</form>



@section Scripts 
{
    <script>
        let featureCounter = @(Model.Features?.Count() ?? 0);
        const pictures = @Html.Raw(Json.Serialize(ViewBag.Pictures ?? new List<string>()));


    </script>
    <script src="~/js/product/porduct-form.js"></script> }

my product-form js

let uploadedImages = []; // { id, url, file?, isMain }


document.addEventListener('DOMContentLoaded', function () {
    try {
        preloadImagesFromServer();
    } catch (error) {
        console.error('Add product page error:', error);
    }
});

// ---------- Features ----------
function addFeature() {
    const container = document.getElementById('featuresContainer');
    if (!container) return;

    const featureId = featureCounter++;
    const featureDiv = document.createElement('div');
    featureDiv.className = 'row g-2 mb-2';
    featureDiv.id = `feature-${featureId}`;
    featureDiv.innerHTML = `
        <div class="col-md-4">
            <input type="text" class="form-control" placeholder="name of feature" required>
        </div>
        <div class="col-md-4">
            <input type="text" class="form-control" placeholder="value" required>
        </div>
        <div class="col-md-4">
            <button type="button" class="btn btn-sm" style="background: #ffebee; color: #c62828; border: none; border-radius: 10px; width: 100%;" onclick="removeFeature(${featureId})">
                <div class="icon-trash-2" style="display: inline-block; font-size: 14px;"></div>
                remove
            </button>
        </div>
    `;
    container.appendChild(featureDiv);
}

function removeFeature(id) {
    const feature = document.getElementById(`feature-${id}`);
    if (feature) feature.remove();
}

// ---------- Preload Images ----------
function preloadImagesFromServer() {
    const preview = document.getElementById('imagesPreview');
    if (!preview) return;

  
    const mainPic = "@ViewBag.Main";

    uploadedImages = [];
    preview.innerHTML = '';

    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 ? '✓ main' : 'choose as main'}
            </button>
        `;
        preview.appendChild(imageItem);
    });

    if (!uploadedImages.some(img => img.isMain) && uploadedImages.length > 0) {
        setMainImage(uploadedImages[0].id);
    }
}

// ---------- Upload New Images ----------
function handleImageUpload(event) {
    const files = Array.from(event.target.files);
    const preview = document.getElementById('imagesPreview');
    if (!preview) return;

    files.forEach(file => {
        const reader = new FileReader();
        reader.onload = function (e) {
            const imageId = uploadedImages.length;
            const isMain = !uploadedImages.some(img => img.isMain); // اولین main
            uploadedImages.push({ id: imageId, url: e.target.result, file: file, isMain: isMain });

            const imageItem = document.createElement('div');
            imageItem.className = 'image-preview-item';
            imageItem.id = `image-${imageId}`;
            imageItem.innerHTML = `
                <img src="${e.target.result}" alt="picture ${imageId + 1}">
                <button type="button" class="remove-btn" onclick="removeImage(${imageId})">×</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(${imageId})">
                    ${isMain ? '✓ main' : 'choose as main'}
                </button>
            `;
            preview.appendChild(imageItem);

            if (isMain) setMainImage(imageId);
        };
        reader.readAsDataURL(file);
    });
}

// ---------- Main Image ----------
function setMainImage(imageId) {
    uploadedImages.forEach(img => img.isMain = false);
    const mainImg = uploadedImages.find(img => img.id === imageId);
    if (mainImg) mainImg.isMain = true;

    uploadedImages.forEach(img => {
        const btn = document.querySelector(`#image-${img.id} .btn`);
        if (btn) {
            btn.style.background = img.isMain ? '#8bc34a' : 'rgba(255,255,255,0.9)';
            btn.style.color = img.isMain ? 'white' : '#666';
            btn.textContent = img.isMain ? '✓ main' : 'choose as main';
        }
    });

    const mainIndexInput = document.getElementById('MainPictureIndex');
    if (mainIndexInput) mainIndexInput.value = imageId;
}

// ---------- Remove Image ----------
function removeImage(imageId) {
    const imageItem = document.getElementById(`image-${imageId}`);
    if (imageItem) imageItem.remove();
    uploadedImages = uploadedImages.filter(img => img.id !== imageId);

    if (uploadedImages.length > 0 && !uploadedImages.some(img => img.isMain)) {
        setMainImage(uploadedImages[0].id);
    }
}

my viewModel

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<string> ExistingProductPictures { get; set; } = new List<string>();

    public List<IFormFile> Pictures { get; set; }=new List<IFormFile>();
    public List<FeatureViewModel> Features { get; set; }=new List<FeatureViewModel>();
}
2
  • 1
    Please fix typos and use punctuation in the question, it is very confusing to read. Commented 3 hours ago
  • You can improve the question by providing a clear and more detailed description of the problem. Include any error messages and the results of any debugging that you have done. If possible, identify where, in the +300 lines of code, you think there is a problem. Commented 1 hour ago

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.