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