使用AJAX和FormData在POST上绑定列表

时间:2019-05-23 17:40:02

标签: javascript c# jquery ajax asp.net-core

我试图将“产品规格”列表绑定到我的“添加产品”对话框。提交是使用AJAX进行的。

由于我允许用户以相同的形式上传产品图片,因此我将发送带有“ FormData”对象的AJAX请求,而不是进行序列化。

结果是由于“规格”列表的格式不符合控制器期望的格式,所以无法执行控制器操作。

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对象时,我需要更改哪些内容来绑定列表?


编辑:添加了规范实体以供故障排除参考。

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 个答案:

答案 0 :(得分:1)

在Controller的操作中修改模型参数以使用[FromForm]属性:

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

您可以阅读有关绑定行为here的更多信息。

更新:

我怀疑您的问题来自两件事:

  1. 您的Spec类没有无参数的构造函数
  2. ProductId为[Required],但您未在POST中传递产品ID

这是我的建议:

您实际上不应该将实体模型放入视图中。 我建议您创建一个名为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传输数据。如果您的项目范围真的很小,那么也许您可能会争辩说要在视图中使用实体模型的情况,但这通常不是一个好主意。