user2023653
user2023653

Reputation:

MVC4: mimicking built-in HtmlHelper extensions, using lambdas

I'm trying to write my own Html helper methods that accept lambda expressions like the built-in helpers (TextBoxFor, CheckBoxFor, LabelFor, etc). I have found some helpful info on this (How I can use a method of a model in a custom HtmlHelper as well as Scott Allen's http://odetocode.com/blogs/scott/archive/2012/11/26/why-all-the-lambdas.aspx), but I'm having trouble putting the pieces together. I'm getting lost in the interplay between the generics and the lambdas. Or something.

My case is a little different from the examples above -- I'm trying to write a data grid editing feature that allows in-place editing of rows in an HTML table. (I've done such a thing in classic ASP, and am trying to implement in MVC.) A grid editing control needs a little bit of state--a row ID and a URL to post changes to. So, my helper methods won't be static extentions of HtmlHelper. At least I don't think so. So, my question is not about grid editing functionality (I already have a grip on that more or less), but really just about how to setup my helper methods so they accept lambdas that behave like the built-in helpers. (I want the benefit of strong typing from my view. I could use reflection and string args to generate my markup, but I know I can do better.)

What I have so far looks like this where I'm trying to use it in a razor view--note the GridEditor object:

@foreach (var item in Model.Items)
{
    var ge = new GridEditor<MyProject.SomethingFromMyDataModel>(item.ID, "someUrl");
    <tr>
        <td>@ge.CheckBox(x => x.SomeBooleanProperty)</td>
        <td>@ge.TextBox(x => x.ATextProperty)</td>
        <td>@ge.DropDownList(x => x.SomeForeignKey, ViewBag.MyList as SelectList)</td>
        <td>@ge.Controls()</td>
    </tr>
}

This does not work, but I hope it gives you an idea of what I'm trying to do. My GridEditor class is setup like this. I'm kind of grasping at straws.... note I did not include everything--but rather just the stuff I thought was relevant.

public class GridEditor<TModel> where <TModel> : class
{
    public GridEditor(int rowID, string postUrl)
    {
    }

    public MvcHtmlString CheckBox<TModel, TValue>(Expression<Func<TModel, TValue>> expression)
    {
    }
}

Anyway, like I said this doesn't work. To boil it down -- my question is -- how do I setup my CheckBox (and similar) methods so that they accept a lambda that's strongly typed with the type passed to my GridEditor?

Upvotes: 0

Views: 139

Answers (1)

user2023653
user2023653

Reputation:

After sleeping on it, I figured out that the answer is pretty easy.... since the methods on my GridEditor are instance (not static), I should use a TModel generic argument on the class definition (like I started with), but then omit it from subsequent method definitions. For example:

public class GridEditor<TModel> : where TModel : class
{
    public MvcHtmlString CheckBox<TValue>(Expression<Func<TModel, TValue>> expression)
    {
    }
}

In use (inside a foreach loop that builds an html table in razor), it looks like this:

@foreach (var pi in Model.Items)
{
    var ge = new GridEditor<AdamOneilSoftware.Models.ProfileItem>("/ServiceProfile/SaveItem", pi.ID);
    <tr>
    <td>@ge.CheckBox(x => x.AutoAdd)</td>
        <td>@ge.DropDownList(x => x.ItemID, ViewBag.ItemID as SelectList)</td>
        <td>@ge.TextBox(x => x.UnitPrice)</td>
        <td>@ge.Controls()</td>
    </tr>
}

Upvotes: 1

Related Questions