简体   繁体   中英

MVC Razor EditorFor Last Object in List

I'm trying to display EditorFor for the last child Object in an collection. Below are the POCOs for the Order (Parent) and Hold (child collection):

public class Order
{
    public int ID {get;set;}
    public string Name {get;set;}
    ....
    public virtual List<Hold> Holds { get; set; }
}

public class Hold
{
    public int ID { get; set; }
    public int OrderID { get; set; }
    public virtual Order Order { get; set; }
    public DateTime? When { get; set; }
    public string Reason { get; set; }
}

Here's my attempt at creating an Order view that shows an Order and the last Hold if there is one present. I've commented out the last Hold attempt that doesn't work.

@model Order

@using (Html.BeginForm("Update", "Order", FormMethod.Post, new {}))
{
   <div class="form-group row">
      @Html.LabelFor(x => x.Name, new { @class = "col-xs-2" })
      <div class="col-xs-10">
         @Html.EditorFor(x => x.Name, new { @class = "form-control"})
      </div>
   </div>

   <div class="form-group row">
      <label class="col-xs-2">When</label>
      <div class="col-xs-10">
         @*@Html.EditorFor(x => x.Holds.Last().When, new {})*@
      </div>
   </div>
}

The Holds collection can also be null so doing Last() in that case will case an null exception even if that did work.

This seems like something simple and I have this pattern in a couple places in my database. Can anyone recommend a good way to handle this situation?

Thanks!

You should use a view model for this because you wont get a very good response in your HttpPost action when you post this back

public class OrderViewModel
{
    public OrderViewModel()
    {
        Order = new Order();
        Hold = new Hold();
    }
    public Order Order { get; set; }
    public Hold Hold { get; set; }
}

public ActionResult Edit(int id)
{

    var o = from o context.Order.Include("Holds").Single(id);
    var model = new OrderViewModel()
    {
        Order = o
    }
    if (o.Holds.Count() > 0)
        model.Hold = o.Holds.Last();
    return View(model);
}

then just use EditorFors

@model OrderViewModel

@using (Html.BeginForm("Update", "Order", FormMethod.Post, new {}))
{
   <div class="form-group row">
      @Html.LabelFor(x => x.Order.Name, new { @class = "col-xs-2" })
      <div class="col-xs-10">
         @Html.EditorFor(x => x.Order.Name, new { @class = "form-control"})
      </div>
   </div>

   <div class="form-group row">
      <label class="col-xs-2>When</label>
      <div class="col-xs-10">
         @Html.EditorFor(x => x.Hold.When)
      </div>
   </div>
}

First, instead of using Last() you should use LastOrDefault() and then do proper null-checking. Last() raises an exception is nothing is found, while LastOrDefault simply returns null in that case.

Second, using Last() or LastOrDefault() will not generate the proper input names via EditorFor , so once you post, the modelbinder won't know what to do with the value. Instead, you need to use indexing:

@if (Model.Holds.Any())
{
    var lastIndex = Model.Holds.Count() - 1;

    <div class="form-group row">
        <label class="col-xs-2">When</label>
        <div class="col-xs-10">
            @Html.EditorFor(x => x.Holds[lastIndex].When, new {})
        </div>
    </div>
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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