Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mvc3 HtmlHelper method to generate formatted Checkbox table

This is a mvc3 Razor project in vb.net. I have a need to create a dynamic table that 1) can contain 1 column to 3 columns and 1 to many rows... The items in the table are checkboxes.. I already have working helper methods in the below class however the method below is jacked up on the return since it just returns the string along with the actual string of the Ntd function, this is there simply for an idea of what should be happening at that point... I am lost as to generate these checkboxes in away that can be binded so the controller post method will save... If I just dump all the checkboxes on page they will all save and update correctly. The layout is just an eye sore..

This is the current view

@ModelType xxxxxx.CourseModel

@Code
    ViewData("Title") = "Edit Courses"
End Code

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"  type="text/javascript"></script>

@Using Html.BeginForm(Nothing, Nothing, FormMethod.Post, New With {.enctype = "multipart/form-data"})
    @Html.ValidationSummary(True)

    @<fieldset>
        <legend>Edit Courses</legend>
        @Html.HiddenFor(Function(model) model.cId)
        <table style="float: left">
            <tr>
                <th>Certification Bodies</th>
            </tr>
            <tr>
                @For _i As Integer = 0 To Model.Bodies.Count - 1
                    Dim i = _i
                    @<td>
                        @Html.CheckBoxFor(Function(model) model.Bodies(i).certSelected)
                        @Html.DisplayFor(Function(f) f.Bodies(i).certName)
                        @Html.HiddenFor(Function(model) model.Bodies(i).certBodyId)
                    </td>
                Next
            </tr>
            <tr>
                <th><input type="submit" value="Save" /></th>
            </tr>
        </table>
    </fieldset>
end using

This is the helper method

<Extension()> _
Public Function CreateCheckBoxTable(ByVal helper As HtmlHelper, ByVal d As List(Of CertBodyVM)) As MvcHtmlString
    Dim htmlDisplayer As String = Table()
    Dim counter As Integer = 0
    For Each item In d
        If counter = 0 Then
            htmlDisplayer = htmlDisplayer + NRow()
        End If
        counter += 1
        If Not counter >= 3 Then
            htmlDisplayer = htmlDisplayer + Ntd("@Html.CheckBoxFor(Function(model) model.Bodies(i).certSelected)@Html.DisplayFor(Function(f) f.Bodies(i).certName)@Html.HiddenFor(Function(model) model.Bodies(i).certBodyId)")
        Else
            counter = 0
            htmlDisplayer = htmlDisplayer + CRow()
        End If
    Next
    htmlDisplayer = htmlDisplayer + CTable()
    Dim x As MvcHtmlString = MvcHtmlString.Create(htmlDisplayer)
    Return x
End Function

Public Function Table() As String
    Return String.Format("<table>")
End Function
Public Function CTable() As String
    Return String.Format("</table>")
End Function
Public Function NRow() As String
    Return String.Format("<tr>")
End Function

Public Function TdEnd() As String
    Return String.Format("</td>")
End Function

Public Function CRow() As String
    Return String.Format("</tr>")
End Function

Public Function Ntd(ByVal text As String) As String
    Return String.Format("<td>{0}</td>", text)
End Function

To call the helper method I just plan to replace the for each loop and its contents with

 @Html.CreateCheckBoxTable(Model.Bodies)

This method is generating the appropriate table with the correct rows and columns but I am lost on the checkboxfor..

Below is the current output that is being generated..

<tr><td><table><tr><td>@Html.CheckBoxFor(Function(model) model.Bodies(i).certSelected)@Html.DisplayFor(Function(f) f.Bodies(i).certName)@Html.HiddenFor(Function(model) model.Bodies(i).certBodyId)</td></table></td></tr>
like image 212
Skindeep2366 Avatar asked Mar 04 '13 03:03

Skindeep2366


1 Answers

Your code won't work. You will have to recompose the lambda expressions in your helper. Also I would recommend you making this CreateCheckBoxTable helper take a lambda expression instead of only a List of CertBodyVM so that the CheckBox (or any other input elements) you might want to use in this table will have proper names:

Public Module MyModule
    Private indexerMethod As MethodInfo = GetType(IList(Of CertBodyVM)).GetMethod("get_Item")
    Private certSelectedProperty As PropertyInfo = GetType(CertBodyVM).GetProperty("CertSelected")
    Private certNameProperty As PropertyInfo = GetType(CertBodyVM).GetProperty("CertName")
    Private certBodyIdProperty As PropertyInfo = GetType(CertBodyVM).GetProperty("CertBodyId")

    <Extension()> _
    Public Function CreateCheckBoxTable(Of TModel)(ByVal helper As HtmlHelper(Of TModel), ByVal ex As Expression(Of Func(Of TModel, IList(Of CertBodyVM)))) As IHtmlString
        Dim table = New TagBuilder("table")
        Dim metadata = ModelMetadata.FromLambdaExpression(ex, helper.ViewData)
        Dim bodies = CType(metadata.Model, List(Of CertBodyVM))
        Dim tableBody = New StringBuilder()
        For i = 0 To bodies.Count - 1
            Dim ex1 = MakePropertyExpression(Of TModel, Boolean)(ex, certSelectedProperty, i)
            Dim ex2 = MakePropertyExpression(Of TModel, String)(ex, certNameProperty, i)
            Dim ex3 = MakePropertyExpression(Of TModel, Integer)(ex, certBodyIdProperty, i)

            Dim tr = New TagBuilder("tr")
            Dim td = New TagBuilder("td")
            td.InnerHtml = String.Concat(
                helper.CheckBoxFor(ex1),
                helper.DisplayFor(ex2),
                helper.HiddenFor(ex3)
            )

            tr.InnerHtml = td.ToString()
            tableBody.Append(tr.ToString())
        Next
        table.InnerHtml = tableBody.ToString()

        Return New HtmlString(table.ToString())
    End Function

    Private Function MakePropertyExpression(Of TModel, TProperty)(ByRef ex As Expression(Of Func(Of TModel, IList(Of CertBodyVM))), ByRef pi As PropertyInfo, ByVal i As Integer) As Expression(Of Func(Of TModel, TProperty))
        Return Expression.Lambda(Of Func(Of TModel, TProperty))(
            Expression.Property(
                Expression.Call(ex.Body, indexerMethod, Expression.Constant(i)),
                pi
            ),
            ex.Parameters()
        )
    End Function
End Module

And then you could use it like this:

@ModelType xxxxxx.CourseModel
...
@Html.CreateCheckBoxTable(Function(x) x.Bodies)
like image 96
Darin Dimitrov Avatar answered Sep 29 '22 05:09

Darin Dimitrov