HtmlHelper MVC entry for IEnumerable <TModel> to display HTML table

I am trying to write an HtmlHelper client that makes it easy to create an Html table from a view passed by IEnumerable <TModel> , where TModel is the type of the model object. I would like the call syntax from the view to have a style:

 @Html.TableFor(model => model.AddedUserID, model => model.ClientID......., model model => AnothreFildIWantDisplayed)); 

I start by trying to get this to work with the first passed expression, like

 @Html.TableFor(model => model.AddedUserID) 

As soon as this works, I will get a comma separated list, working using parameters and taking more than one expression.

My code looks like this:

 @using MyNameSpaceToMyHelper @model IEnumerable<User> @{ ViewBag.Title = "Index"; } <p> </p> @Html.TableFor(model => model.AddedUserID)); 

My attempt to write HtmlHelper:

 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.UI; namespace MyNameSpaceToMyHelper { public static class GridExtensions { public static MvcHtmlString TableFor<TModel, TValue>(this HtmlHelper<IEnumerable<TModel>> html, Expression<Func<TModel, TValue>> expression) { var writer = new HtmlTextWriter(new StringWriter()); writer.RenderBeginTag(HtmlTextWriterTag.Table); writer.RenderBeginTag(HtmlTextWriterTag.Thead); writer.RenderBeginTag(HtmlTextWriterTag.Th); writer.RenderBeginTag(HtmlTextWriterTag.Td); //Colum Headers writer.Write(html.DisplayNameFor(expression)); writer.RenderEndTag(); writer.RenderEndTag(); writer.RenderEndTag();//Close THead //Column Data //html.DisplayFor((Expression<Func<TModel, TValue>>)expression); foreach (ViewDataDictionary<TModel> vdm in html.ViewData.Values) { writer.RenderBeginTag(HtmlTextWriterTag.Tr); writer.RenderBeginTag(HtmlTextWriterTag.Td); //DOESN'T COMPILE html.DisplayFor(expression); writer.RenderEndTag(); writer.RenderEndTag(); } writer.RenderEndTag(); //Close Header return new MvcHtmlString(writer.InnerWriter.ToString()); } } } 

This does not compile due to:

Html.DisplayFor (expression);

As far as I understand, this is due to the fact that the HtmlHelper is passed to my method for HtmlHelper<IEnumerable<TModel>> , whereas when using DisplayFor it should be HtmlHelper<TModel> .

Because of this error, it seems possible that I could get this to work by splitting the table header and data into two separate method calls, and from my view, I could call the header method and then have a for loop inside the actual view that calls the method for each line or something else. Something like that

 @using MyNameSpaceToMyHelper @model IEnumerable<User> @{ ViewBag.Title = "Index"; } <p> </p> @Html.TableHeadersFor(model => model.AddedUserID)); @foreach (var item in Model) { @Html.TableRowsFor(model => model.AddedUserID)); } 

This should work because TableHeadersFor can call DisplayNameExtensions.DisplayNameFor , which accepts HtmlHelper<IEnumerable<TModel>> and TableRowsFor can accept HtmlHelper<TModel> and calls DisplayExtensions.DisplayFor with HtmlHelper<TModel> , so the problem of incompatible HtmlHelper<TModel> types will disappear.

However, I ideally do not want to do this, if at all possible, since I want the call syntax to be as simple as possible. The main premise of my question is to try to maintain a simple syntax for calling without writing a for loop in the view and duplicating expressions to select columns. The whole goal here is to create something reusable and as simple as possible for others, as much as possible.

I looked in ILSpy on how DisplayNameExtensions.DisplayNameFor works the way it handles HtmlHelper<IEnumerable<TModel>> , because it looks like an exstension method that accepts HtmlHelper<IEnuermable<TModel>> calls the extension method internally on HtmlHelper<TModel> , but the ILSpy code produces doesn’t even recompile, so I don’t understand how .NET can do this.

Is this possible in one extension method, or will I be forced to break it into a compromise of the calling syntax?

UPDATE 1 I managed to get the following work with a pretty big job:

 @Html.BeginTableFor(model => model.AddedUserID , model => model.Active , new { @class = "grid" }) @foreach (var item in Model) { @Html.DisplayRowFor(model => item.AddedUserID , model => item.Active) } @Html.EndTableFor() 

This is not as concise as we would like, and includes a lot of work, especially because each BeginTableFor and DisplayRowFor expression can return a different type, meaning that I basically had to implement an overload method for any number of expressions (I made up to 100, so up to 100 columns will work!). This is basically the same problem as in .Net with the Action and Func delegates, where they had to write different versions for a different number of parameters, for example. Action T1, T2, ..., T16. This is obviously very messy, but I'd rather have a good call syntax than avoid many method overloads.

+4
source share
1 answer

Take a look at this article in a specific direction and change to suit your needs.

First you need the HTML extensions as follows:

 public static class RazorExtensions { public static HelperResult List<T>(this IEnumerable<T> items, Func<T, HelperResult> template) { return new HelperResult(writer => { foreach (var item in items) { template(item).WriteTo(writer); } }); } } 

Then in your HTML you can do the following:

 { var comics = new[] { new ComicBook {Title = "Groo", Publisher = "Dark Horse Comics"}, new ComicBook {Title = "Spiderman", Publisher = "Marvel"} }; } <table> @comics.List( @<tr> <td>@item.Title</td> <td>@item.Publisher</td> </tr>) </table> 
+1
source

Source: https://habr.com/ru/post/1490940/


All Articles