views:

723

answers:

4

Hello, I would like to extend ASP.NET MVC's TextBox so it conditionally switches its CSS value (based on data from the Model). However, I don't want to put that logic in my view (by conditionally changing htmlAttributes/via method call there), but instead extend the class so I don't clutter my view. Basically I want the TextBox on creation look at the Model to see if it has a dictionary entry with its own name, and then look up the associated CSS value if it finds it.

The problems I encounter are:

1) How do I extend it? The TextBox itself is already an extension method and not a class so I can't "inherit"?

2) How would I communicate the conditions affecting the TextBox's attributes from my controller method to the TextBox?

+1  A: 

One thing you could do is to use ViewData to pass the desired CSS class to your view, then the view wouldn't need to be aware of your logic to do this.

To answer your specific questions,

1: You are right, you cannot extend this, but here are two alternatives: one is to avoid the need for extending this at all, and making use of what is provided (such as using ViewData), and the other is to implement your own. You can download the MVC source code and work directly from there if you'd like.

2: Consider something in your controller:

if (someCondition)
{
    ViewData["someClass"] = "foo";
}
else
{
    ViewData["someClass"] = "snazzle";
}

and then in your view:

<%= Html.TextBox("myField", "30", new { @class = ViewData["someClass"] }) %>

Best of luck!
-f!

Funka
I knew already about No. 2 and wanted to avoid this though (as I wrote in my question). I'll probably try to look at/work with the MVC source code directly unless somebody else has a pointer on how to do this.
Alex
+3  A: 

Probably you should not couple your text box back to the model. Always it’s better idea to have dependencies just in one way. From model to view.

So indirect answer is to have yet another extension method that will accept enum or boolean and depending on this will add or not class name.

<%= Html.StyledTextBox("myField", ViewData["ShouldBeStyled"]) %>

Also there is direct answer.

To extend ASP.NET MVC, you should write your own extension methods. You can always call underline extension method. So your code could look like:

public static class MyCoolExtension
{
  public static string TextBox(this HtmlHelper htmlHelper, string name)
  {
     // get data from htmlHelper.ViewData and call another extension methods of the HtmlHelper 
    var className = htmlHelper.ViewData["someClass"];
    return htmlHelper.TextBox("myField", "30", new { @class = className });
  }
}
Mike Chaliy
A: 

Depending on how important it is to you, you could also use jQuery to apply formatting. It is very easy to select specific textboxes.

Is say "Depending on how important it is to you", because there is a group of users that have JavaScript disabled (that cannot be neglected), in which case the formatting won't be applied.

Guillaume Hanique
A: 

This is why I'm so unhappy with the MVC team's decision to use strings everywhere. Adding a class to a textbox should not be the concern of the TextBox HtmlHelper, it should be a separate aspect of building HTML. Here is a better solution using a semantic model to define Html. I'm going to use TagBuilder here, but I highly recommend looking into the HtmlTags library included with FubuMvc.

public TagBuilder TextBox(this HtmlHelper helper, string name, string value)
{
   var tag = new TagBuilder("input");
   tag.MergeAttribute("type", "text");
   tag.MergeAttribute("name", name);
   tag.MergeAttribute("value", value);
   return tag;
}

Now since we're returning a semantic (TagBuilder) model, we can continue to modify the model before converting it to an HTML literal. ConditionallyAddClass is a reusable extension method that will apply a class to any TagBuilder model:

public TagBuilder ConditionallyAddClass<T>(this TagBuilder tag, string @class, T model, Func<T, bool> condition)
{
   if(condition(model))
   {
      tag.AddCssClass(@class);
   }
   return tag;
}

In your view you can now do this (assuming StyleAgeTextbox is a boolean property):

<%= Html.TextBox("Age", Model.Age).ConditionallyAddClass("customClass", Model, m => m.StyleAgeTextbox) %>

The view engine would automatically call ToString() on the TagBuilder to get the proper HTML.

I hope this gives you the basic idea. There are of course many ways you can extend this. You could have a standard naming convention and use a dynamic type to get around using expressions or you could have lookup tables or strongly type the entire thing to use member expressions (eg TextBoxFor(m => m.Age)). I prefer moving the entire thing into an html convention framework like Fubu does, but that's probably beyond most projects.

Ryan