首页 > 解决方案 > 当涉及不同模型时,如何将主视图和部分视图值传递给控制器​​?

问题描述

原始问题: 这可能是一件简单的事情,但到目前为止我已经寻找了 30 多个小时,但找不到任何适合我的合适答案(也尝试了很多方法)。

所以,我想要实现的是我必须在视图上显示一些数据,并在同一个视图中获取一些值。为此,我创建了一个主视图,它显示一些数据并接受一些值。也是主视图中的部分视图,它还显示一些数据并接受一些值。

主视图和部分视图的模型不同,相关的数据库表也不同。

主视图模型:

public class VMBooking : VMBase
    {
        public int BookingID { get; set; }
        public Guid BookingGUID { get; set; }
        public int NoOfChildrenArrived { get; set; }
       ...
        public List<VMBookingGuest> VMBookingGuests { get; set; }
       ...
    }

局部视图模型

 public class VMBookingGuest
    {
        public int BookingGuestID { get; set; }
        public int BookingID { get; set; }
        public int GuestID { get; set; }
        public string GuestName { get; set; }
        public string GuestCNIC { get; set; }
        public bool IsCNIC { get; set; }
        public bool IsGuestArrived { get; set; }
        public bool IsGuestDeparted { get; set; }
    }

现在我已成功通过局部视图中的局部视图模型,并且我的数据也正在显示中..

我的主视图 CSHTML:

 @using VMBooking  
<form class="form-horizontal" id="validation-form" method="post" defaultbutton="btnSubmit">
          @Html.HiddenFor(c => c.BookingID)
          @Html.HiddenFor(c => c.BookingGUID)
                           
          @Html.Partial("_Arrival", Model.VMBookingGuests)
                                   
          <div class="form-group">
          <input asp-for="@Model.NoOfChildrenArrived" type="text" id="NoOfChildrenArrived" />
          </div>    
          <button type="submit" id="btnSubmit">Submit</button>
    </form>

部分视图 CSHTML:

 @foreach (var item in Model)
            {
                <tr class="center">
                    <td>@item.GuestName</td>
                    <td>@item.GuestCNIC</td>
                    <td>
                        <div>
                            <input id="IsGuestArrived" name="IsGuestArrived" asp-for="@item.IsGuestArrived" type="checkbox"/>
                        </div>
                    </td>
                </tr>
            }

对应的控制器动作如下

 [HttpGet]
        public IActionResult Arrival(int id)
        {
            VMBooking model = _BookingRepo.GetBookingByID(id);
            model.VMBookingGuests = _BookingRepo.GetGuestinfo(id);
            PopulateDropdowns(model);
            return View(model);
        }

        [HttpPost]
        public IActionResult Arrival(VMBooking vmBooking)
        {
            VMBooking model = _BookingRepo.GetBookingByID(vmBooking.BookingID);
            model.VMBookingGuests = _BookingRepo.GetGuestinfo(vmBooking.BookingID);
            if (model != null)
            {
                _BookingRepo.ArrivalUpdate(vmBooking);
                foreach (var item in model.VMBookingGuests)
                {
                    _BookingRepo.GuestArrivalUpdate(item);
                }
                return RedirectToAction("Index");
            }
            PopulateDropdowns(model);
            return View();
        }
  

一切正常,但问题出现在我必须在主视图上的单个提交按钮上提交此组合输入数据(来自主视图和部分视图)的地方。当我按下提交按钮时,只有我的主视图中的值被传递给控制器​​,而不是部分视图。

请注意,对于每个访客条目,我必须将复选框值列表 (id="IsGuestArrived") 传递给控制器​​。

如前所述,我尝试了许多不同的方法,但没有一个对我有用。所以我问,实现这一目标的合适方法是什么?

编辑: 我已经找到了我的查询的答案,现在我想显示我根据@KingKing 的建议对我的代码所做的更改......

我所做的是在我的主视图中插入部分标签而不是@html.partial,所以我的主视图的代码如下

 @using VMBooking  
    <form class="form-horizontal" id="validation-form" method="post" defaultbutton="btnSubmit">
              @Html.HiddenFor(c => c.BookingID)
              @Html.HiddenFor(c => c.BookingGUID)
                               
              <partial name="_Arrival" for="VMBookingGuests" />
                                       
              <div class="form-group">
              <input asp-for="@Model.NoOfChildrenArrived" type="text" id="NoOfChildrenArrived" />
              </div>    
              <button type="submit" id="btnSubmit">Submit</button>
        </form>

至于我的部分观点,我同意了

  @foreach (var item in Model)
                {
                <input type="hidden" asp-for="@Model[i].GuestID" />
                <input type="hidden" asp-for="@Model[i].BookingID" />

                    <tr class="center">
                        <td>@item.GuestName</td>
                        <td>@item.GuestCNIC</td>
                        <td>
                            <div>
                                <input id="IsGuestArrived" name="IsGuestArrived" asp-for="@item.IsGuestArrived" type="checkbox"/>
                            </div>
                        </td>
                    </tr>
                }

注意我在局部视图中使用的隐藏值。

在我的控制器内而不是使用

 model.VMBookingGuests = _BookingRepo.GetGuestinfo(vmBooking.BookingID);

我用了

    model.VMBookingGuests = vmBooking.VMBookingGuests;

因为没有隐藏值,来宾 ID 无法被识别。

标签: c#asp.net-mvcasp.net-corepartial-viewspass-data

解决方案


EditorFor 助手可能更适合您尝试做的事情,但我建议先简化一些事情(然后您可以走那条路)。

所以,而不是这个(顺便说一下,它会生成一些无效的标记):

@using VMBooking  
<form class="form-horizontal" id="validation-form" method="post" defaultbutton="btnSubmit">
    @Html.HiddenFor(c => c.BookingID)
    @Html.HiddenFor(c => c.BookingGUID)
                   
    @Html.Partial("_Arrival", Model.VMBookingGuests)
                           
    <div class="form-group">
        <input asp-for="@Model.NoOfChildrenArrived" type="text" id="NoOfChildrenArrived" />
    </div>    
    <button type="submit" id="btnSubmit">Submit</button>
</form>

使用这样的东西:

@using VMBooking  
<form class="form-horizontal" id="validation-form" method="post" defaultbutton="btnSubmit">
    <input type="hidden" asp-for="BookingID" />
    <input type="hidden" asp-for="BookingGUID" />
                           
    <table>
        @for (int i = 0; i < Model.VMBookingGuests.Count; i++)
        {
            <tr class="center">
                <td>@Model.VMBookingGuests[i].GuestName</td>
                <td>@Model.VMBookingGuests[i].GuestCNIC</td>
                <td>
                    <div>
                        <input type="checkbox" asp-for="VMBookingGuests[i].IsGuestArrived" />
                        <input type="hidden" asp-for="VMBookingGuests[i].BookingGuestID" />
                    </div>
                </td>
            </tr>
        }
    </table>
                                   
    <div class="form-group">
        <input asp-for="NoOfChildrenArrived" type="text" id="NoOfChildrenArrived" />
    </div>    
    <button type="submit" id="btnSubmit">Submit</button>
</form>

您需要某种带有名称VMBookingGuests[0].IsGuestArrived等的标记,以便在处理 POST 操作时正确绑定模型,并且只有在视图中具有某种成功控制的属性才会被提交 - 因此添加属性的隐藏输入BookingGuestID。还有很多其他资源可以解释为什么你需要这种命名风格,但要点是,如果你有这样的东西(作为实际提交的输入的名称):

VMBookingGuests[0].IsGuestArrived
VMBookingGuests[1].IsGuestArrived
VMBookingGuests[4].IsGuestArrived
VMBookingGuests[5].IsGuestArrived

然后模型绑定器停止VMBookingGuests从索引“损坏”的点重建列表 - 在该示例中,当索引从 1 跳转到 4 时。它也只会从类似的东西构建列表VMBookingGuests[0].SomePropertyHere- 你不能只是有一个输入从每个客人的名字命名SomePropertyHere,也没有类似的SomePropertyHere[]工作。

现在,您在 POST 操作中有这个:

foreach (var item in model.VMBookingGuests)
{
    _BookingRepo.GuestArrivalUpdate(item);
}

这似乎表明您将“数据”模型用作“视图”模型,并依靠您的 ORM 来确定发生了什么变化。在这种情况下,您需要为来宾模型的每个属性添加隐藏字段。如果可以选择编写“服务”式方法,那么您实际上只需要 ID 属性(BookingGuestID在您的情况下为 )和IsGuestArrived属性(因为这是唯一应该更改的内容)。像这样的方法的一个例子可能是:

public bool UpdateGuestArrivals(int bookingID, params VMBookingGuest[] guests)
{
    bool success = false;

    if(guests?.Any() == true)
    {
        foreach(var guest in guests)
        {
            var bookingGuest = nameofyourDbContexthere.VMBookingGuests.SingleOrDefault(m => m.BookingID == bookingID && m.BookingGuestID == guest.BookingGuestID);
            if(bookingGuest != null)
            {
                bookingGuest.IsGuestArrived = guest.IsGuestArrived;
            }
        }
 
        nameofyourDbContexthere.SaveChanges();
        success = true;
    }

    return success;
}

该示例中有很多假设,但我认为这个想法很明确。您可能可以将其更改为使用您的 repo 类。


推荐阅读