简体   繁体   中英

Create a table with n number of columns using a Razor template

I need to create a table of data where I know the number of rows that will be present, but the number of columns will vary. The table will exist inside of a razor template.

Ideally I would use a single foreach loop to iterate over a collection and inside the loop, I would add a column for each object.

Here is the way I have it working right now, but this requires one loop per row. With 30ish rows, this is not an ideal solution.

<table>
  <tr>
    @foreach(var o in oCollection)
    {
      <td>@o.Name</td>
    }
  </tr>
  <tr>
    @foreach(var o in oCollection)
    {
      <td>@o.Id</td>
    }
  </tr>
  <tr>
    @foreach(var o in oCollection)
    {
      <td>@o.Address</td>
    }
  </tr>
</table>

NOTE: I use an HTML table in my example but that is not a requirement. Only that the data is displayed in a table like output.

EDIT: Here's a fiddle example of the output for 2 objects. http://jsfiddle.net/rked00dr/ . Here's an example for 3 objects. http://jsfiddle.net/rked00dr/2/

What you're looking for is calling data pivoting. Rather trying to generate it in Razor view you can handle this in your Controller and use pivoted data to avoid the complexity that you already mentioned.

Let say your data looks like:

            var list = new List<Entity>();
            list.Add(1, "Thing1", "WithAddress_1");
            list.Add(2, "Thing2", "WithAddress_2");
            list.Add(3, "Thing3", "WithAddress_3");
            list.Add(4, "Thing4", "WithAddress_4");
            list.Add(5, "Thing5", "WithAddress_5");
            list.Add(6, "Thing6", "WithAddress_6");

Note: Don't worry about this initialization I used list extension to get this done.

Use a function like below though it's not the best approach to Pivot data but it'll do the needful. Create an extension method like below. Make sure you replace the List<Entity> to you type.

Public static class ListExtensions
 {
    Public static IEnumerable<dynamic> ToPivot(this List<Entity> oCollection)
    {
        List<dynamic> parent = new List<dynamic>();
        List<dynamic> extended = null;

        foreach (Entity entity in oCollection)
        {
            extended = new List<dynamic>();
            foreach (var prop in entity.GetType().GetProperties())
            {
                extended.Add(new { Property = prop.Name, Value = prop.GetValue(entity) });
            }

            parent.AddRange(extended);
        }

        // Grouping of value by property names, so each property
        // represents list of all values w.r.t to that property in given list.
        var grps = from d in parent
                   group d by d.Property
                       into grp
                       select new
                       {
                           Property = grp.Key,
                           Values = grp.Select(d2 => d2.Value).ToArray()
                       };
        return grps;
    }
}

Now you can simply call this function to your collection.

  <table>
    foreach (var item in oCollection.ToPivot())
    {
       <tr>
        <td>
           @item.Property;
        <td>
        foreach (var value in item.Values)
        {
            <td>
                @value;
           </td>
        }
       <tr>
    }
  </table>

Output:

+---------+---------------+---------------+-----+
| Id      | 1             | 2             | ... |
| Name    | Thing1        | Thing2        | ... |
| Address | WithAddress_1 | WithAddress_2 | ... |
+---------+---------------+---------------+-----+

my suggested generic version (as per comment below -> jim):

public static IEnumerable<dynamic> ToPivot<T>(this IList<T> oCollection)
{
    var parent = new List<dynamic>();

    try
    {
        foreach (T entity in oCollection)
        {
            var extended = new List<dynamic>();
            foreach (var prop in entity.GetType().GetProperties())
            {
                extended.Add(new { Property = prop.Name, Value = prop.GetValue(entity) });
            }

            parent.AddRange(extended);
        }

        // Grouping of value by property names, so each property
        // represents list of all values w.r.t that property in given list.
        var grps = parent.GroupBy(d => d.Property).Select(grp => new
        {
            Property = grp.Key,
            Values = grp.Select(d2 => d2.Value).ToArray()
        });
        return grps;
    }
    catch
    {
        // log the reason perhaps
        return default(dynamic);
    }
}

this would allow you to only have to iterate the collection once to build your columns..

@{
    string row1;
    string row2;
    string row3;
    foreach (var o in oCollection)
    {
        row1 += string.Format("<td>{0}</td>", o.Name);
        row2 += string.Format("<td>{0}</td>", o.Id);
        row3 += string.Format("<td>{0}</td>", o.Address);
    }
}
<table>
    <tr>
        @Html.Raw(row1);
    </tr>
    <tr>
        @Html.Raw(row2);
    </tr>
    <tr>
        @Html.Raw(row3);
    </tr>
</table>

If you want to get functional, you could do something like this. Where oCollectionItem is whatever type is in oCollection

@{
    List<Func<oCollectionItem, string>> funcs = new List<Func<oCollectionItem, string>>(){
        x => x.Name,
        x => x.Address,
        x => x.ID.ToString(), 
       //repeat for each thing to display
    };
}


<table>
    @foreach(var func in funcs)
    {
        <tr>
            @foreach(var item in oCollection)
            {
              <td>@func(item)</td>
            }
        </td>    
    }
</table>

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