Return a viewmodel with IEnumerable's to view

2019-09-11 11:57发布

问题:

In a razor view, how can I loop through different IEnumerables in a ViewModel and display their variables as columns in the same row?

I Return an instance of a viewmodel which is populated with IEnumerables of different models to a mvc view like so:

Viewmodel:

namespace example.ViewModels
{

    public class ClaimModel
    {
        public IEnumerable<Claim> Claims { get; set; }
        public IEnumerable<Member> Members { get; set; }
        public IEnumerable<ID> IDs { get; set; }

    }
}

My controller which return the view looks like this:

namespace example.Controllers
{
    public class OfficeManagerController : Controller
    {
        private Entities db = new Entities(); //This is my database

        public ActionResult Index()
        {
            IEnumerable<Member> member = db.Members.OrderByDescending(b => b.ClaimID);
            IEnumerable<Claim> claim = db.Claims.OrderByDescending(b => b.ClaimID);
            IEnumerable<ID> id = db.IDs;
            ClaimModel model = new ClaimModel { IDs = id, Members = member, Claims = claim };

            return View(model);
        }
    }
}

Here is a snippet of my razor view where i loop through one of the IEnumerables in my viewmodel called Members:

@model example.ViewModels.ClaimModel

<div class="table list-group ">
    <div class="row table-bordered ">
        <div class="pull-left col-xs-1 h5">
            Claim ID:
        </div>
        <div class="pull-left col-xs-2 h5">
            Member Name:
        </div>
        <div class="pull-left col-xs-2 h5">
            Member ID:
        </div>

    </div>

    @foreach (var item in Model.Members)
    {

        <div class="pull-left col-xs-1 h5">
            @Html.DisplayFor(modelItem => item.ClaimID)
        </div>
        <div class="pull-left col-xs-2 ">
            @Html.DisplayFor(modelItem => item.Name)
        </div>
        <div class="pull-left col-xs-2 ">
            @Html.DisplayFor(modelItem => item.MemberID)
        </div>

    }

</div>

This is a regular way of looping through one of the IEnumerables. Of course I don't need a ViewModel to accomplish this because I might as well just pass my Members IEnumerable straight into my view.

Now what logic do I have to apply in order to display data from the three different IEnumerables into columns of the same row? I imagine its possible with razor.

I need something like this:

@model example.ViewModels.ClaimModel

<div class="table list-group ">
    <div class="row table-bordered ">
        <div class="pull-left col-xs-1 h5">
            Member Name:
    </div>
    <div class="pull-left col-xs-2 h5">
        Claim Name:
    </div>
    <div class="pull-left col-xs-2 h5">
        ID Name:
    </div>
</div>

@foreach (var item in Model.Members)
{
    <div class="pull-left col-xs-1 h5">
        @Html.DisplayFor(modelItem => item.Name)
    </div>
}
@foreach (var item in Model.Claims)
{
    <div class="pull-left col-xs-1 h5">
        @Html.DisplayFor(modelItem => item.Name)
    </div>
}
@foreach (var item in Model.IDs)
{
    <div class="pull-left col-xs-1 h5">
        @Html.DisplayFor(modelItem => item.Name)
    </div>
}

However this will not insert my variables into the same rows but will stack all the members names next to one another and so on.

I am adding here thanks to comment below that every Claim has a Member and an ID. Claim has a primary key called ClaimID and Member and ID are related to it with foreign keys called ClaimID.

回答1:

Assuming all three collections has same item count and you want to show the nth item in all the lists on the nth row of the table in 3 columns, this should work.

<table class="table table-striped">
    @{ var rowCounter = 0;}
    @foreach (var item in Model.Members)
    {
        <tr>
            <td>
                 @item.Name
            </td>
            <td>
                @if (Model.Claims.Count()>= rowCounter)
                {
                    <span>@Model.Claims[rowCounter].Name</span>
                }    
            </td>
                <td>
                @if (Model.IDs.Count()>= rowCounter)
                {
                    <span>@Model.IDs[rowCounter].Name</span>
                }
            </td>

        </tr>
        rowCounter++;
    }
</table>

While this solve your problem, I strongly suggest you change your approach. You should do a join in your controller and send a list of items with all these 3 properties and do a simple loop and display in your razor view.

So create a view model to represent your grid data

public class ClaimRecord
{
  public string ClaimName {set;get;}
  public string MemberName {set;get;}
  public int Id {set;get;}
}

and in your action method, join the three data sets and project the needed results a collection of this class and send to the view

var list = new List<ClaimRecord>();
//to do: Add items to the list here 
// I don't know the relation between these 3 data sets. So i can't tell yo how to do that.
// But basically, if you have forign key relationship or so, you need to do a join
return View(list);

and in your view

@model List<ClaimRecord>
<table>
@foreach(var item in Model)
{
  <tr>
     <td>@item.Id</td>
     <td>@item.ClaimName</td>
     <td>@item.MemberName</td>
  </tr>
}
</table>

EDIT : As per the comment.

Claim has a primary key called ClaimID and Member and ID are related to it with foreign keys called ClaimID.

You can do a join with your LINQ expression.

var list = (from a in db.Claims
    join b in db.Members on a.ClaimID equals b.ClaimID 
    join c in db.IDs on a.ClaimID equals c.ClaimID 
    select new ClaimRecord {
                            Id = a.ClaimID,  //Update these as needed
                            MemberName = b.MemberName,
                            ClaimName = a.ClaimName }).ToList();

return View(list);

Please update the projection part with your property names.(My code assumes that Member table/Entity has a MemberName column/property and Claim table has a ClaimName property. Update that part according to your needs.