Привязка списка в POST с помощью AJAX и FormData - PullRequest
1 голос
/ 23 мая 2019

Я пытаюсь привязать список «Спецификации продукта» к моему диалоговому окну «Добавить продукт». Отправка осуществляется с помощью AJAX.

Поскольку я разрешаю пользователю загружать изображение продукта в эту же форму, вместо сериализации я отправляю свой запрос AJAX с объектом FormData.

В результате действие контроллера никогда не достигается из-за того, что список спецификаций не соответствует формату, ожидаемому контроллером.

ProductModalViewModel

public class ProductModalViewModel
{
    public ProductModalViewModel()
    {
        Product = new Product();
        Specs = new List<Spec>();
    }
    public Product Product { get; set; }

    //Other properties removed for brevity

    public List<Spec> Specs { get; set; }
}

Вид (модальный)

<form id="formSaveProduct" onsubmit="SaveProduct(event)" enctype="multipart/form-data">

<input type="hidden" asp-for="Product.Id" />

//Removed other form fields for brevity

<div class="specs-list-group">
    <ul class="list-group">
        @for (int i = 0; i < Model.Specs.Count(); i++)
        {
            <li class="list-group-item">
                <input type="hidden" asp-for="@Model.Specs[i].Id" />
                <div class="row">
                    <div class="col-5">
                        <input type="text" asp-for="@Model.Specs[i].Name" />
                    </div>
                    <div class="col-5">
                        <input type="text" asp-for="@Model.Specs[i].Value" />
                    </div>
                </div>
            </li>
        }
    </ul>
</div>
</form>

Сценарий

function SaveProduct(e) {
        e.preventDefault();  // prevent standard form submission

        $.ajax({
            url: "@Url.Action("SaveProduct", "ProductManagement", new { Area = "Admin" })",
            method: "post",
            data: new FormData($('#formSaveProduct')[0]),
            contentType: false,
            processData: false,
            success: function (result) {
                if (result.success) {
                    $("#exampleModal").modal('toggle');
                    location.reload();
                }
                else {
                    $(".modal-body").html(result);
                }
            },
            error: function (e) {
                alert("Error: " + e.status)
            }
        });
    }

Контроллер

 [HttpPost]
 public ActionResult SaveProduct(ProductModalViewModel model)
 {
     //Save
 }

Заголовок запроса

FormData:

Product.Id: 1
Product.Brand: Browning
Product.Model: Gold Mossy Oak Shadow Grass Blades
Product.ProductNum: 723654
Product.CategoryId: 4
Product.IsActive: true
Product.Overview: This is an overview of the tkjsldfgn jlfdgl-sdfgn lkjgfnjkl dfsngkl kjlngkldf jngjkln kdfjnggf h sd sdfgdf...
Specs[0].Id: 1
Specs[0].Name: Test Spec 1
Specs[0].Value: Test Value 1
Specs[1].Id: 2
Specs[1].Name: Test Spec 2
Specs[1].Value: Test Value 2
Specs[2].Id: 3
Specs[2].Name: Test Spec 3
Specs[2].Value: Test Value 3

Эта форма работает отлично, пока я не добавлю в список спецификаций. Что мне нужно изменить, чтобы связать список при использовании объекта FormData?


РЕДАКТИРОВАТЬ: Добавлен Spec Entity для справки по устранению неполадок.

public class Spec
{
    [Key]
    public int Id { get; set; }

    public Spec(string name, string value)
    {
        Name = name;
        Value = value;
    }

    [Required]
    public string Name { get; set; }

    [Required]
    public string Value { get; set; }

    [ForeignKey("Product")]
    [Required]
    public int ProductId {get; set; }
    public virtual Product Product { get; set; }

    //Timestamps
    public DateTime? Created { get; set; }
    public DateTime? Modified { get; set; }
}

1 Ответ

1 голос
/ 24 мая 2019

Измените параметр модели в действии вашего контроллера, чтобы использовать атрибут [FromForm]:

[HttpPost]
public ActionResult SaveProduct([FromForm]ProductModalViewModel model)
{
    //Save
}

Подробнее о поведении привязки можно прочитать здесь .

ОБНОВЛЕНИЕ:

Я подозреваю, что ваша проблема вызвана двумя причинами:

  1. Ваш класс Spec не имеет конструктора без параметров
  2. ProductId [Обязательный], но вы не передаете идентификатор продукта в своем POST

Вот мое предложение:

Вы действительно не должны бытьпомещая модели Entity в ваши представления.Я бы посоветовал вам создать новый класс с именем SpecDto и следовать этим правилам

  1. Добавлять только те свойства, которые вы хотите отправить из клиента
  2. Есть конструктор без параметров.
  3. Используйте только соответствующие атрибуты.Не используйте связанные с сущностью атрибуты, такие как [ForeignKey("Product")]

Например:

public class SpecDto
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public string Value { get; set; }

    public int ProductId { get; set; }

    public DateTime? Created { get; set; }
    public DateTime? Modified { get; set; }
}

И вы должны сделать то же самое для продукта.

Тогда ваша ViewModel должнавыглядят так:

public class ProductModalViewModel
{
    public ProductModalViewModel()
    {
        Product = new ProductDto();
        Specs = new List<SpecDto>();
    }

    public ProductDto Product { get; set; }
    public List<SpecDto> Specs { get; set; }
}

В вашем контроллере вы затем переносите данные из классов DTO в ваши модели сущностей.Я знаю, это кажется утомительным, но это правильный способ делать такие вещи.Если у вас большой проект, используйте AutoMapper для передачи данных.Если ваш проект действительно мал по объему, то , возможно, , вы можете привести доводы в пользу использования моделей сущностей в ваших представлениях, но, как правило, это не очень хорошая идея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...