简体   繁体   English

当涉及不同型号时,如何将主视图值和部分视图值传递给 controller?

[英]How to pass main and partial views values to the controller when different models are involved?

Original Question: This might be a simple thing to do but I have been looking for it for more than 30 hrs by now and couldn't find any suitable answer that works for me (tried a tons of ways as well).原始问题:这可能是一件简单的事情,但到目前为止我已经寻找了 30 多个小时,但找不到任何适合我的合适答案(也尝试了很多方法)。

So, what I am trying to achieve is that I have to display some data on a view and also take in some values within the same view.所以,我想要实现的是我必须在视图上显示一些数据,并在同一个视图中获取一些值。 For this I have created a main view, this displays some data and takes in some values.为此,我创建了一个主视图,它显示一些数据并接受一些值。 Also a partial view within the main view, which also displays some data and takes in some values.也是主视图中的部分视图,它还显示一些数据并接受一些值。

The Models for main view and Partial View are different and the related DB tables as well.主视图和部分视图的模型不同,相关的数据库表也不同。

Main View Model:主视图 Model:

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; }
       ...
    }

Partial View Model局部视图 Model

 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; }
    }

Now I have successfully passed the partial view model in the partial view and my data is being displayed as well..现在我已经成功通过了局部视图中的局部视图 model 并且我的数据也正在显示中..

My Main View CSHTML:我的主视图 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>

Partial View CSHTML:部分视图 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>
            }

The corresponding controller action is as follows对应的controller动作如下

 [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();
        }
  

Things are working fine, but the problem arises where I have to submit this combined input data (from main and partial view) on a Single submit button, which is on the main view.一切正常,但问题出现在我必须在主视图上的单个提交按钮上提交此组合输入数据(来自主视图和部分视图)上。 When i press the submit button only the values from my main view are passed to the controller and not of the partial view.当我按下提交按钮时,只有我的主视图中的值被传递给 controller 而不是部分视图。

Note that I have to pass a list of Check Box values (id="IsGuestArrived") to the controller for every guest entry.请注意,对于每个访客条目,我必须将复选框值列表 (id="IsGuestArrived") 传递给 controller。

And as said before I have tried a number of different ways but none of them is working for me.如前所述,我尝试了许多不同的方法,但没有一个对我有用。 So I am asking, What would be the suitable way to achieve this?所以我问,实现这一目标的合适方法是什么?

Edit: I have found the answer to my query and now i would like to display the changes that i made to my code on the suggestion of @KingKing...编辑:我找到了我的查询的答案,现在我想根据@KingKing 的建议显示我对代码所做的更改...

What i did was I inserted partial tag instead of @html.partial in my main view, so the code for my main view goes as我所做的是在我的主视图中插入部分标签而不是@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>

And as for my partial view i went with至于我的部分观点,我同意了

  @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>
                }

notice the Hidden values that i have used inside my partial view.注意我在局部视图中使用的隐藏值

And within my controller instead of using而在我的 controller 内而不是使用

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

I used我用了

    model.VMBookingGuests = vmBooking.VMBookingGuests;

Because without the hidden values the guest IDs were not being recognized.因为没有隐藏值,来宾 ID 无法被识别。

The EditorFor helper is probably a better fit for what you're trying to do, but I would suggest simplifying things first (then you can go that route). EditorFor 助手可能更适合您尝试做的事情,但我建议先简化一些事情(然后您可以 go 这条路线)。

So, instead of this (which generates some invalid markup, by the way):所以,而不是这个(顺便说一下,它会生成一些无效的标记):

@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>

use something like this:使用这样的东西:

@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>

You need some sort of markup with names like VMBookingGuests[0].IsGuestArrived etc. in order for the values to be correctly model-bound when the POST action is being processed, and only properties which have some sort of successful control in the view will be submitted - hence adding the hidden input for the BookingGuestID property.您需要某种名称的标记,例如VMBookingGuests[0].IsGuestArrived等,以便在处理 POST 操作时正确绑定模型,并且只有在视图中具有某种成功控制的属性才会提交 - 因此添加了BookingGuestID属性的隐藏输入。 There's quite a few other resources that will explain why you need that naming style, but the gist is that if you have something like this (as the names of the inputs actually submitted):还有很多其他资源可以解释为什么你需要这种命名风格,但要点是,如果你有这样的东西(作为实际提交的输入的名称):

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

Then the modelbinder stops rebuilding the VMBookingGuests list from the point where the indexing is "broken" - in that example, when the index jumps from 1 to 4. It will also only build the list from something like VMBookingGuests[0].SomePropertyHere - you can't just have an input named SomePropertyHere from each guest, nor does something like SomePropertyHere[] work.然后,modelbinder 停止从索引“损坏”的点重建VMBookingGuests列表 - 在该示例中,当索引从 1 跳转到 4 时。它也只会从VMBookingGuests[0].SomePropertyHere类的内容构建列表 - 你不能只从每个客人那里获得一个名为SomePropertyHere的输入,也不能像SomePropertyHere[]这样的东西起作用。

Now, you have this in the POST action:现在,您在 POST 操作中有这个:

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

That would seem to indicate that you're using your "data" models as "view" models, and relying on your ORM to figure out what's changed.这似乎表明您将“数据”模型用作“视图”模型,并依靠 ORM 来找出发生了什么变化。 In that case, you'll need to add hidden fields for every property of the guest model.在这种情况下,您需要为来宾 model 的每个属性添加隐藏字段。 If writing a "service"-style method is an option, you really only need the ID property ( BookingGuestID , in your case) and the IsGuestArrived property (since that's the only thing that should be changing).如果可以选择编写“服务”式方法,那么您实际上只需要 ID 属性(在您的情况下为BookingGuestID )和IsGuestArrived属性(因为这是唯一应该更改的内容)。 An example of a method like that might be:像这样的方法的一个例子可能是:

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;
}

There's a lot of assumptions in that example, but I think the idea is clear.该示例中有很多假设,但我认为这个想法很明确。 You can probably change that to use your repo class.您可能可以更改它以使用您的存储库 class。

Elements rendered in partial views should be able to be rendered with their names prefixed with the correct path.在局部视图中呈现的元素应该能够以它们的名称以正确的路径为前缀来呈现。 It's a bit tricky with Html.Partial (will have a solution at the end). Html.Partial有点棘手(最后会有解决方案)。 But if you use the tag helper <partial> , it would be easier by using the for attribute (note it's different from model attribute which will not pass along the current name path), like this:但是,如果您使用标签助手<partial> ,使用for属性会更容易(注意它与model属性不同,后者不会沿当前名称路径传递),如下所示:

<partial name="_Arrival" for="VMBookingGuests"/>

Your partial view need to be updated as well, because you're mapping an array, you need to use indices to access each array member so that the name for each item can be rendered correctly (with the index), like this:您的局部视图也需要更新,因为您正在映射一个数组,您需要使用索引来访问每个数组成员,以便可以正确呈现每个项目的名称(使用索引),如下所示:

<!-- NOTE: this requires the view model of your partial view must support accessing item by index, like an IList -->
@for(var i = 0; i < Model.Count; i++) {
            <tr class="center">
                <td>@Model[i].GuestName</td>
                <td>@Model[i].GuestCNIC</td>
                <td>
                    <div>
                        <input id="IsGuestArrived-@i" asp-for="@Model[i].IsGuestArrived" type="checkbox"/>
                    </div>
                </td>
            </tr>
}

Now it should work expectedly because the element names are rendered correctly like VMBookingGuests[0].IsGuestArrived , ...现在它应该可以正常工作,因为元素名称正确呈现,如VMBookingGuests[0].IsGuestArrived ,...

Now for Html.Partial , you can still use it but you need to pass in the prefix info via a ViewDataDictionary , you need to build that prefix by your own, but it's easy because we use just a standard method to get that, like this:现在对于Html.Partial ,您仍然可以使用它,但您需要通过ViewDataDictionary传入前缀信息,您需要自己构建该前缀,但这很容易,因为我们只使用标准方法来获取它,就像这样:

@{
    var vd = new ViewDataDictionary(ViewData);
    vd.TemplateInfo.Prefix = Html.NameFor(e => e.VMBookingGuests);
}

<!-- for Html.Partial -->
@Html.Partial("_Arrival", Model.VMBookingGuests, vd)

Now with that prefix info passed along, the element names are also rendered correctly as when using <partial> .现在,随着前缀信息的传递,元素名称也可以像使用<partial>时一样正确呈现。 Of course you still need the same corrected code for your partial view above.当然,对于上面的部分视图,您仍然需要相同的更正代码。

I think you should change the definition of a partial view as follows我认为您应该将部分视图的定义更改如下

@Html.Partial("_Arrival", Model)

And then set the partial view code as follows to send the data to the controller correctly.然后设置部分视图代码如下,将数据正确发送到controller。

@using VMBooking  
@foreach (var item in Model.VMBookingGuests)
{
     <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>
}

If that doesn't work, use the code below如果这不起作用,请使用下面的代码

[HttpPost]
public IActionResult Arrival(VMBooking vmBooking, VMBookingGuest[] VMBookingGuests)
{
    .................................
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM