Add table generator post
This commit is contained in:
parent
bef4972c91
commit
c9a67c1205
138
src/posts/TableAutoGenerator.md
Normal file
138
src/posts/TableAutoGenerator.md
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
---
|
||||||
|
title: ASP.NET Core Table Generator
|
||||||
|
description: A tag helper that takes a list and generates a table
|
||||||
|
tags: [ Programming, C#, ASP.NET Core ]
|
||||||
|
---
|
||||||
|
|
||||||
|
This tag helper takes any `IEnumerable` and will use reflection to generate a table for it. This will pick up on attributes in the model for customizing the output.
|
||||||
|
|
||||||
|
## How To Use
|
||||||
|
|
||||||
|
The model in the `IEnumerable`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class Thing
|
||||||
|
{
|
||||||
|
// Don't make a column for this property
|
||||||
|
[Display(AutoGenerateField = false)]
|
||||||
|
public int ThingId { get; set; }
|
||||||
|
|
||||||
|
// Use "Thing Name" for the column header instead of "ThingName"
|
||||||
|
[Display(Name = "Thing Name")]
|
||||||
|
public string ThingName { get; set; }
|
||||||
|
|
||||||
|
// Use string.Format with the DataFormatString
|
||||||
|
[DisplayFormat(DataFormatString = "{0:C}")]
|
||||||
|
public decimal Price { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then use the tag helper in the view:
|
||||||
|
|
||||||
|
```cshtml
|
||||||
|
<table asp-for="Things"></table>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
The tag helper:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the <see cref="For"/> is a list, fill the table with the list data with
|
||||||
|
/// columns for each property.
|
||||||
|
/// <para>
|
||||||
|
/// Properties can be hidden from the table by adding <c>[Diaplay(AutoGenerateField = false)]</c>
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// The column heading for each property is the property name by default. This can be overriden by
|
||||||
|
/// adding <c>[Display(Name = "Property Name")]</c>
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
[HtmlTargetElement("table", Attributes = "asp-for")]
|
||||||
|
public class TableTagHelper : TagHelper
|
||||||
|
{
|
||||||
|
[HtmlAttributeName("asp-for")]
|
||||||
|
public ModelExpression For { get; set; }
|
||||||
|
|
||||||
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
|
{
|
||||||
|
var modelType = this.For.ModelExplorer.ModelType.GenericTypeArguments[0];
|
||||||
|
var list = this.For.Model as IEnumerable<object>;
|
||||||
|
|
||||||
|
if (list is null)
|
||||||
|
output.TagName = "";
|
||||||
|
|
||||||
|
// Don't display properties that have AutoGenerateField set to false
|
||||||
|
IList<PropertyInfo> properties = modelType.GetProperties()
|
||||||
|
.Where(p => !p.GetCustomAttributes<DisplayAttribute>(false)
|
||||||
|
.Any(a => a.GetAutoGenerateField() == false))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Header row
|
||||||
|
List<TagBuilder> tableHeadCells = properties
|
||||||
|
.Select(p =>
|
||||||
|
{
|
||||||
|
string displayName = p.GetCustomAttributes<DisplayAttribute>(false)
|
||||||
|
.Where(a => !string.IsNullOrEmpty(a.GetName()))
|
||||||
|
.FirstOrDefault()
|
||||||
|
?.GetName()
|
||||||
|
?? p.Name;
|
||||||
|
|
||||||
|
TagBuilder th = new("th");
|
||||||
|
th.InnerHtml.Append(displayName);
|
||||||
|
|
||||||
|
return th;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
TagBuilder theadTr = new("tr");
|
||||||
|
tableHeadCells.ForEach(th => theadTr.InnerHtml.AppendHtml(th));
|
||||||
|
|
||||||
|
TagBuilder thead = new("thead");
|
||||||
|
thead.InnerHtml.AppendHtml(theadTr);
|
||||||
|
output.Content.AppendHtml(thead);
|
||||||
|
|
||||||
|
|
||||||
|
// Content rows
|
||||||
|
List<TagBuilder> tableRows = list
|
||||||
|
.Select(item =>
|
||||||
|
{
|
||||||
|
List<TagBuilder> cells = properties
|
||||||
|
.Select(p =>
|
||||||
|
{
|
||||||
|
TagBuilder td = new("td");
|
||||||
|
|
||||||
|
var content = p.GetValue(item);
|
||||||
|
string dataFormatString = p.GetCustomAttributes<DisplayFormatAttribute>(false)
|
||||||
|
.Where(a => !string.IsNullOrWhiteSpace(a.DataFormatString))
|
||||||
|
.Select(a => a.DataFormatString)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (dataFormatString is not null)
|
||||||
|
td.InnerHtml.Append(string.Format(dataFormatString, content));
|
||||||
|
else if (content is not null)
|
||||||
|
td.InnerHtml.Append(content.ToString());
|
||||||
|
|
||||||
|
return td;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
TagBuilder tr = new("tr");
|
||||||
|
cells.ForEach(td => tr.InnerHtml.AppendHtml(td));
|
||||||
|
return tr;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
TagBuilder tbody = new("tbody");
|
||||||
|
tableRows.ForEach(tr => tbody.InnerHtml.AppendHtml(tr));
|
||||||
|
output.Content.AppendHtml(tbody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
Loading…
Reference in a new issue