MVC 3 Razor将父/子表显示为一个表

时间:2012-07-20 18:58:07

标签: asp.net-mvc-3 entity-framework razor

我的数据库中有两个2个表

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    |

这些是相关模型

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

我可以通过在视图中进行以下操作,轻松地在格式表中显示它

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

哪个输出

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

我真正看到的输出结果如下。

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

有人可以指出我正确的方向吗?

2 个答案:

答案 0 :(得分:4)

也许存在更优雅的解决方案,但这就是我将如何做到的。

您需要一个循环来生成Foo.Number标题行,然后您需要第二个循环,您可以选择所有Bar并按Name对它们进行分组。您可以从这些组中生成数据行。

现在你只需要一个遍历Bar.Value的thrid循环并构建表格行。

所以代码中的上述“算法”:

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

注意:我添加了OrderBy(b => b.FooID),以确保Bar值与Foo.Number标题正确对齐。

结果如下:

enter image description here

答案 1 :(得分:0)

这是一种可能的方法......我正在努力想出一个使用标准ActionResult的解决方案(虽然我确定它是可能的)所以我认为返回一个扁平的匿名json对象可能会成功。在Controller上粘贴一个JsonResult方法,通过jQuery调用它然后构建你的表。此解决方案取决于按Bar.Name排序的记录(提前警告一些未经测试的代码!)

编辑:我刚刚意识到这不太正确,需要重构。当有相同数量的Bar itms时它可以工作,但实际上每个Foo可能会有一个变量数,因此添加到每一行的元素数量需要等于Bar项目的最大数量。

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

然后在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;
}

您可以使用它来测试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;
}