I have a reliable and tested import method between an .xls spreadsheet[1] that returns a DataTable
. I've located this in my service layer, not data as only the workbook gets persisted as an uploaded file, but now I'm wondering where and how to produce an HTML representation of this DataTable
. I would prefer to avoid two loops in my view. I have a variable number of columns in the DataTable
.
[1] Using an OleDb interface. I know it's archaic but it works.
DataTables are weakly typed. I hate weak typing.
Here's an alternative approach: using view models, strongly typed views and display templates. So start by defining a view model which will represent the information you are willing to display:
public class MyViewModel
{
public IEnumerable<ColumnViewModel> Columns { get; set; }
public IEnumerable<RowViewModel> Rows { get; set; }
}
public class ColumnViewModel
{
public string Name { get; set; }
}
public class RowViewModel
{
public IEnumerable<CellValueViewModel> Values { get; set; }
}
public class CellValueViewModel
{
public string Value { get; set; }
}
then you could have a controller action which will populate this view model. If you already have some service layers that spits DataTables, you could map those tables to the aforementioned view model. For the purpose of this demo let's hardcode:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Columns = new[]
{
new ColumnViewModel { Name = "column1" },
new ColumnViewModel { Name = "column2" },
new ColumnViewModel { Name = "column3" },
},
Rows = new[]
{
new RowViewModel
{
Values = new[]
{
new CellValueViewModel { Value = "1x1" },
new CellValueViewModel { Value = "1x2" },
new CellValueViewModel { Value = "1x3" },
}
},
new RowViewModel
{
Values = new[]
{
new CellValueViewModel { Value = "2x1" },
new CellValueViewModel { Value = "2x2" },
new CellValueViewModel { Value = "2x3" },
}
},
new RowViewModel
{
Values = new[]
{
new CellValueViewModel { Value = "3x1" },
new CellValueViewModel { Value = "3x2" },
new CellValueViewModel { Value = "3x3" },
}
}
}
};
return View(model);
}
}
and the last part is the view (~/Views/Home/Index.cshtml
):
@model MyViewModel
<table>
<thead>
<tr>
@Html.DisplayFor(x => x.Columns)
</tr>
</thead>
<tbody>
@Html.DisplayFor(x => x.Rows)
</tbody>
</table>
and our display templates:
~/Views/Home/DisplayTemplates/ColumnViewModel.cshtml
:
@model ColumnViewModel
<th>
@Html.DisplayFor(x => x.Name)
</th>
~/Views/Home/DisplayTemplates/RowViewModel.cshtml
:
@model RowViewModel
<tr>
@Html.DisplayFor(x => x.Values)
</tr>
~/Views/Home/DisplayTemplates/CellValueViewModel.cshtml
:
@model CellValueViewModel
<td>
@Html.DisplayFor(x => x.Value)
</td>
And that's pretty much all. As you can see we have written exactly zero loops in our views and we ended up with a nice <table>
structure representing an Excel Worksheet.