如何将项目列表从视图传递到控制器(ASP.NET MVC 4)

Posted

技术标签:

【中文标题】如何将项目列表从视图传递到控制器(ASP.NET MVC 4)【英文标题】:How to Pass a List of Items from View to Controller (ASP.NET MVC 4) 【发布时间】:2016-09-10 06:42:49 【问题描述】:

我正在尝试将多个 List 类型的 item 传递给我的控制器,但是,当我提交我的数据时,它在我的控制器中显示为 null .

我希望在视图中有一个“费用”列表,每个“费用”或项目旁边都有一个来自模型中 Submitted 布尔属性的复选框。当我检查项目时,我希望在数据库中更新检查项目的属性列表 SubmittedDateSubmitted

视图中的

@Html.DisplayFor(modelItem => item.Submitted) 会生成复选框。

我做错了什么?

这是我的观点:

@model IEnumerable<Expenses.Models.Expense>

@
ViewBag.Title = "Submit Expenses";
Layout = "~/Views/Shared/_Layout.cshtml";

DateTime today = DateTime.Today;
string formattedDate = today.ToString("MM/dd/yyyy");


<link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>

<h2>Submit Expenses</h2>

@using (Html.BeginForm())

@Html.AntiForgeryToken()
<div class="form-inline">
    <div class="form-group">
        <h4>Start Date:</h4>
        <div class="col-md-10">
            @Html.TextBox("expenseDate", formattedDate, htmlAttributes: new
           
               @class = "form-control"
           )
        </div>
    </div>

    <div class="form-group">
        <h4>End Date:</h4>
        <div class="col-md-10">
            @Html.TextBox("expenseDate2", formattedDate, htmlAttributes: new
           
               @class = "form-control"
           )
        </div>
    </div>

    <div class="form-group">
        @if (User.IsInRole("admin"))
        
            <h4>Username:</h4>
            <div class="editor-field">
                @Html.DropDownList("UserId", String.Empty)
            </div>
        
    </div>

    <br />
    <br />

    <div class="form-group">
        <div class="col-md-2">
            <input type="submit" value="Retrieve Expenses" class="btn btn-default" />
        </div>
    </div>

</div>

<br />
<br />


<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table style="margin-bottom: 20px;">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Company)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Category)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Province)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ReceiptName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Comment)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.GrossAmount)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.TaxAmount)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.NetAmount)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Mileage)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.TravelStatus)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.LunchLearnStatus)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.WithClientStatus)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.DateEntered)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.DateSubmitted)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ExpenseDate)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ImageName)
        </th>
        @if (User.IsInRole("admin"))
        
            <th>
                @Html.DisplayNameFor(model => model.UserProfile.UserName)
            </th>
        
        <th></th>
    </tr>
    <tr>
        <td>
            <b>Select All:</b>
            <br />
            <input type="checkbox" name="expense" value="Expense" id="selectAllCheckboxes" class="expenseCheck">
        </td>
    </tr>
    @foreach (var item in Model)
    
        <tr>
            <td class="submitCheck">
                @Html.EditorFor(modelItem => item.Submitted)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Company)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Category)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Province)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReceiptName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Comment)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.GrossAmount)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.TaxAmount)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.NetAmount)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Mileage)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.TravelStatus)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.LunchLearnStatus)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.WithClientStatus)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.DateEntered)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.DateSubmitted)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ExpenseDate)
            </td>
            <td>
                @if (item.ImageName != null)
                
                    <a href="~/Images/@Html.DisplayFor(modelItem => item.UserProfile.FullName)/@Html.DisplayFor(modelItem => item.ImageName)" class="imageClick" target="_blank"><img src="~/Images/@Html.DisplayFor(modelItem => item.UserProfile.FullName)/@Html.DisplayFor(modelItem => item.ImageName)"  style="width:80%" /></a>
                
            </td>
            @if (User.IsInRole("admin"))
            
                <td>
                    @Html.DisplayFor(modelItem => item.UserProfile.UserName)
                </td>
            
            <td>
                @Html.ActionLink("Edit", "Edit", new  id = item.ExpenseId ) |
                @Html.ActionLink("Details", "Details", new  id = item.ExpenseId ) |
                @if (User.IsInRole("admin"))
                
                    @Html.ActionLink("Delete", "Delete", new  id = item.ExpenseId )
                

            </td>
        </tr>
    

</table>

 @Html.ActionLink("Submit Expenses", "SubmitExpenses", "Expenses", null, new  @class = "submitLink", @style = "background-color: #d3dce0; border: 1px solid #787878; cursor: pointer; font-size: 1.5em; font-weight: 600; margin-right: 8px; padding: 7px; width: auto; text-decoration: none; font-weight:bold;")

<div class="ExportSection" style="margin-top:30px;">
    @*<a href="javascript:void(0)">Export To CSV</a>*@
    @Html.ActionLink("Export To CSV", "ExportExpensesListToCSV")
</div>


@section scripts 
<script type="text/javascript">


    $("#selectAllCheckboxes").click(function () 
        $('.submitCheck input:checkbox').not(this).prop('checked', this.checked);
    );

    $(function () 
        $("#expenseDate").datepicker();
    );

    $(function () 
        $("#expenseDate2").datepicker();
    );

</script>


这是我的控制器方法:

public ActionResult SubmitExpenses(List<Expense> expenses, DateTime? expenseDate = null, DateTime? expenseDate2 = null, int? userId = 0)
    
        expenseDate = (DateTime)Session["FirstDate"];
        expenseDate2 = (DateTime)Session["SecondDate"];

        if (expenseDate == null || expenseDate2 == null)
        
            expenseDate = DateTime.Now.AddMonths(-1);
            expenseDate2 = DateTime.Today;
        

        string currentUserId = User.Identity.Name;

        var query = from e in db.Expenses
                    join user in db.UserProfiles on e.UserId equals user.UserId
                    where e.ExpenseDate >= expenseDate && e.ExpenseDate <= expenseDate2 && e.DateSubmitted == null
                    orderby e.ExpenseDate descending
                    select new  e, user ;

        if (User.IsInRole("admin") && userId != 0)
        
            query = query.Where(x => x.user.UserId == userId);
        
        else if (!User.IsInRole("admin"))
        
            query = query.Where(x => x.user.UserName == currentUserId);
        

        var expensesFromView = expenses;

        var joined = from dbExpense in query.Select(x => x.e).AsEnumerable()
                     join localExpense in expenses on dbExpense.ExpenseId equals localExpense.ExpenseId
                     where localExpense.Submitted
                     select dbExpense;

        foreach (Expense exp in joined)
        
            exp.DateSubmitted = DateTime.Today;
        

        try
        
            db.SaveChanges();
            return RedirectToAction("Index");
        
        catch (Exception e)
        
            Console.WriteLine(e);
            return RedirectToAction("Submit");
        

    

这是我的模型:

[Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int ExpenseId  get; set; 

    [Required]
    public string Company  get; set; 

    [Required]
    public string Category  get; set; 

    [Required]
    [Display(Name = "Receipt Name")]
    public string ReceiptName  get; set; 
    public string Comment  get; set; 

    [Required]
    public string Province  get; set; 

    [Required]
    [Display(Name = "Gross Amount")]
    public decimal GrossAmount  get; set; 

    [Required]
    [Display(Name = "GST/HST Amount")]
    public decimal TaxAmount  get; set; 

    [Required]
    [Display(Name = "Net Amount")]
    public decimal NetAmount  get; set; 

    [Display(Name = "Mileage (in Kilometers)")]
    public int Mileage  get; set; 

    [Display(Name = "Travelling?")]
    public bool TravelStatus  get; set; 

    [Display(Name = "Lunch & Learn?")]
    public bool LunchLearnStatus  get; set; 

    [Display(Name = "With Clients?")]
    public bool WithClientStatus  get; set; 

    [DisplayFormat(DataFormatString = "0:MM/dd/yyyy")]
    [Required]
    [Display(Name = "Date Entered")]
    public DateTime? DateEntered get; set; 

    [DisplayFormat(DataFormatString = "0:MM/dd/yyyy")]
    [Display(Name = "Date Submitted")]
    public DateTime? DateSubmitted  get; set; 

    [DisplayFormat(DataFormatString = "0:MM/dd/yyyy")]
    [Required]
    [Display(Name = "Expense Date")]
    public DateTime? ExpenseDate  get; set; 

    [Display(Name = "Image")]
    public string ImageName  get; set; 

    [Display(Name = "Submitted?")]
    public bool Submitted  get; set; 

    [Required]
    [Display(Name = "Name")]
    public int UserId  get; set; 
    [ForeignKey("UserId")]
    public virtual UserProfile UserProfile  get; set; 

我的变量 expensesFromView 显示为 null,因此,我的 joined 查询出现错误:

值不能为空。参数名称:inner

【问题讨论】:

【参考方案1】:

我真的找不到将项目列表直接从 View 传递到 Controller 的方法,所以我决定使用 AJAX。

我将控制器的参数从 List 类型更改为 int[ ] 以获取项目 ID 数组:

public ActionResult SubmitExpenses(int[] expenseIDs, DateTime? expenseDate = null, DateTime? expenseDate2 = null, int? userId = 0)
    
        expenseDate = (DateTime)Session["FirstDate"];
        expenseDate2 = (DateTime)Session["SecondDate"];

        if (expenseDate == null || expenseDate2 == null)
        
            expenseDate = DateTime.Now.AddMonths(-1);
            expenseDate2 = DateTime.Today;
        

        string currentUserId = User.Identity.Name;

        var query = from e in db.Expenses
                    join user in db.UserProfiles on e.UserId equals user.UserId
                    where e.ExpenseDate >= expenseDate && e.ExpenseDate <= expenseDate2 && e.DateSubmitted == null
                    orderby e.ExpenseDate descending
                    select new  e, user ;

        if (User.IsInRole("admin") && userId != 0)
        
            query = query.Where(x => x.user.UserId == userId);
        
        else if (!User.IsInRole("admin"))
        
            query = query.Where(x => x.user.UserName == currentUserId);
        

        //var localExpenseIDs = expenseIDs;

        var joined = from dbExpense in query.Select(x => x.e).AsEnumerable()
                     join localExpense in expenseIDs on dbExpense.ExpenseId equals localExpense
                     where localExpense == dbExpense.ExpenseId
                     select dbExpense;

        foreach (Expense exp in joined)
        
            exp.DateSubmitted = DateTime.Today;
            exp.IsSubmitted = true;
        

        try
        
            db.SaveChanges();
            return RedirectToAction("Index");
        
        catch (Exception e)
        
            Console.WriteLine(e);
            return RedirectToAction("Submit");
        

    

在我看来,我将每个项目的 ID 分配给它自己的 HTML 复选框的 ID:

@foreach (var item in Model)
    
        <tr>
            <td class="checkbox-td">
                @Html.CheckBox("isSubmitted", new
           
               @id = @Html.DisplayFor(modelItem => item.ExpenseId),
               @class = "submitBox"
           )
            </td>
        </tr>
     

<div>
    @Html.ActionLink("Submit Expenses", "", "", null, new  @id = "submitExpensesLink" )
</div>

我写了一些 jQuery,以便每个选中的复选框都会将输入元素的 id 添加到数组中,并且整数数组将 POSTSubmitExpenses强>行动:

var checkedArray = [];

    $(':checkbox[name=isSubmitted]').on('change', function () 

        checkedArray = $(':checkbox[name=isSubmitted]:checked').map(function () 
                return this.id;
         )
        .get();


        //alert(checkedArray);
    );

    $('#submitExpensesLink').click(function () 

        $.ajax(
            type: "POST",
            traditional: true,
            url: "@Url.Action("SubmitExpenses", "Expenses")",
            data:  expenseIDs: checkedArray ,
        success: function () 
            alert("Success!");
        ,
        error: function (XMLHttpRequest, textStatus, errorThrown) 
            if (debug) 
                alert(XMLHttpRequest.responseText);
                alert(textStatus);
                alert(errorThrown);
            
        
    );
    )

【讨论】:

【参考方案2】:

有两种方法可以做到这一点 -

1 .

您可能需要这个非常好且可扩展的扩展 - MvcCheckBoxList -- 好像这个网站已经被关闭了 :) 参考选项 2

2.

@for (int i = 0; i < Model.Count; i++)


     <tr>
      <td class="submitCheck">
       <input type="checkbox" value="@Model[i].Id" name="Expense[@i].Id">
       <input type="hidden" value="0" name="Expense[@i].Id">          
      </td>
      <td>
          @Html.DisplayFor(modelItem => item.Company)
      </td>
     </tr>

顺便说一句:如果您使用第二种方式:在您的控制器中,过滤费用对象 ID 的零值;因为如果它没有被选中,零会被发送到控制器(作为元素的 ID)宽限期到您可以在上面的代码中看到的“隐藏”字段。

【讨论】:

谢谢哥们。你给我的链接似乎是我需要的。我会试试这个,如果它有效,请告诉你!【参考方案3】:

1) 将FormMethod.Post 添加到@using (Html.BeginForm())

@using (Html.BeginForm("nameAction", "nameController", FormMethod.Post))

   //Your  code

2) 您应该为这样的输入添加名称:

<input type="text" name="someName"/>

【讨论】:

感谢您的回答。我已经尝试过了,但数据仍然没有正确传递。

以上是关于如何将项目列表从视图传递到控制器(ASP.NET MVC 4)的主要内容,如果未能解决你的问题,请参考以下文章

如何在asp.net mvc中将数据从视图传递到控制器

ASP .NET MVC 3 - 将参数从视图传递到控制器

将 ID 从视图传递到控制器 (ASP.NET)

通过组合动作链接和 ASP.Net MVC 5 中的输入将用户键入的输入从视图传递到动作控制器?

ASP.NET MVC 视图需要将选定的下拉列表和日历日期传递给模型

将多个参数从 ajax post 传递到 asp.net mvc 控制器