MVC 3 Razor Displaying parent/child tables as one table

I have two 2 tables in my database

Table: Foo                 Table: Bar
-----------------          ---------------------
|FooID     | Int|          |BarID     | Int    |
|Number    | Int|          |FooID     | Int    |
-----------------          |Name      | String |
                           |Value     | Int    |
                           ---------------------

With data                  with data
|FooID | Number |          |BarID |FoodID |Name    |Value |
|1     | 1      |          |1     |1      |apple   |100   |
|2     | 2      |          |2     |1      |orange  |110   |
                           |3     |2      |apple   |200   |
                           |4     |2      |orange  |40    |

These are the related models

class Foo
{
    public int FooID { get; set; }
    public int Number { get; set;}

    public virtual ICollection<Bar> Bars { get; set; }
}

class Bar
{
    public int BarID { get; set; }
    public int FooID { get; set; }
    public string Name { get; set;}
    public int Value { get; set;}
}

I can easly display this in a table like format by doing the follwing in the view

<table>
    @foreach(var f in Model)
    {
        <tr>
            foreach(var b in f.Bar)
            {
                <td>@f.Number</td>
                <td>@b.Name</td>
                <td>@b.Value</td>
            }
        </tr>
    }
</table>

Which outputs

-------------------
|1   |apple    |100|
-------------------
|1   |orange   |110|
-------------------
|2   |apple    |200|
-------------------
|2   |orange   | 40|
-------------------

What I'd really to see for the output is the following.

-------------------------
|         |  1      |  2 |
-------------------------
|apple    |100      |200 |
-------------------------
|orange   |200      | 40 |
-------------------------

Can someone please point me in the right direction???

Answers


Maybe there are more elegant solutions exist, but here is how I would do it.

You need one loop to generate the header rows from Foo.Number then you need a second loop where you select all the Bars and group them by their Name. From these groups you can generate the data rows.

Now you only need a thrid loop which goes through the Bar.Values and builds the table row.

So the above described "algorithm" in code:

<table>
    <tr>
        <td>
            &nbsp;
        </td>
        @foreach (var f in Model.OrderBy(f => f.FooID))
        {
            <td>@f.Number
            </td>
        }
    </tr>
    @foreach (var group in Model.SelectMany(f => f.Bars).GroupBy(b => b.Name))
    {
        <tr>
            <td>@group.Key
            </td>
            @foreach (var b in group.OrderBy(b => b.FooID))
            {
                <td>@b.Value
                </td>
            }
        </tr>
    }
</table>

Note: I've added the OrderBy(b => b.FooID) to make sure that the Bar values correctly aligned with the Foo.Number headers.

And this how the result looks like:


Here's one possible way of doing it...I was struggling to come up with a solution using a standard ActionResult (although I'm sure it's possible) so I thought returning a flattened anonymous json object might do the trick. Stick a JsonResult method on your Controller, call it via jQuery then build your table. This solution depends on the records being sorted by Bar.Name (warning some untested code ahead!)

EDIT: I've just realised this isn't quite correct and needs to be refactored. It works when there are the same number of Bar itms, but in reality there will probably be a variable number per Foo, so the number of elements added to each row needs to equal the maximum number of Bar items.

public JsonResult GetBarsForFoos()
{
    var foos = from f in repositoryOrContext.Foos.Include("Bar")
        select f;

    return foos == null
    ? Json(new object[0], JsonRequestBehavior.AllowGet)
    : Json(foos.Bars.OrderBy(bar => bar.Name).Select(
        bar => new { foos.Number, bar.Name, bar.Value }).ToArray(),
        JsonRequestBehavior.AllowGet);
}

Then in jQuery:

function populateFoos() {
    $.ajax({
        url: '/Foo/GetBarsForFoos',
        async: false
    }).done(function (data) {
        // have an empty table in your Razor with an id of tblFoos
        var fooTable = $('#tblFoos');
        fooTable.html('');
        fooTable.append(createFooTable(data));
    }).error(function (msg, url, line) {
        alert("Error - error message: " + line);
    });
}

function createFooTable(data) {
    var html = '',
    len = data.length,
    currentBarName = '',
    allBarNumbers = [];

    html = '<table><tr><td></td>';

    // Create 1st row that contains foo.Number
    for (var i = 0; i < len; i++) {

        if (allBarNumbers.indexOf(data[i].Number) === -1) {
            allBarNumbers.push(data[i].Number);

            html += '<td>' + data[i].Number + '</td>';
        }           
    }

    html += '</tr>';

    // Create further rows that line up Name and Value under their foo.Number values
    for (var x = 0; x < len; x++) {

        if (x === 0) {
            html += '<tr>';
        }

        // grab Bar name if changed
        if (currentBarName != data[x].Name) {
            currentBarName = data[x].Name;

            if (x !== 0) {
                html += '</tr><tr>';
            }
            html += '<td>' + currentBarName + '</td>';
        }

        html += '<td>' + data[x].Value + '</td>';
    }

    html += '</tr>/<table>';

    return html;
}

You can use this to test createFooTable:

function testFooTable() {

    var data = [
        { Number: 1, Name: 'apple', Value: 100 },
        { Number: 2, Name: 'apple', Value: 200 },
        { Number: 1, Name: 'orange', Value: 110 },
        { Number: 2, Name: 'orange', Value: 40 }
    ];

    var fooTable = createFooTable(data);

    return fooTable;
}

Need Your Help

Use current AVCaptureSession on WebRTC on iOS devices

ios avfoundation webrtc avcapturesession

I'm going to explain my current project and what I want to do.

How to Converting SqlCommand Type To Int Type

c# sql-server-2005 sqlcommand

I Want To Convert SqlCommand Result into Int Value How Can i Do that??