So I’m really enjoying working with the new Asp.Net MVC framework its refreshing to not be fighting the Asp.Net page life cycle for a change.
One of the things that I don’t like about the MVC Framework is it love affair with strings. Jeremy Miller puts it far more eloquently than I ever could so I definitely recommend you read that article on his approach with working with the MVC framework even if it is a few months old now.
So like Jeremy I’m a big fan of passing strongly typed objects between my view and my controller. The other day I was working on a page where I wanted to send an email. Before the email had been sent I wanted to render out a series of TextBoxes to capture the email information. Once it had been sent I then wanted to make the textbox read-only. I thought it would be good to write an HtmlHelper extension method with a fluent interface so that I could pass a condition (as a Lambda) into it so that I could make a declaration something like code below.
This code example uses the MVC Futures assembly which you can utilize by referencing the Microsoft.Web.Mvc assembly in your solution.
<%= Html.ConditionalTextBoxFor(model => model.FromEmail)
.MakeReadOnlyIf(() => Model.Sent) %>
So how did I implement this. First of all I had to write the HtmlHelper extension method which looks like this.
public static IControlForOptions ConditionalTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> action) where TModel : class { return new TextBoxForOptions<TModel, TProperty>(helper, action); }
This method returns an IControlForOptions interface which looks like this. Using an interface means we can provide different concrete implementations for different controls.
public interface IControlForOptions{ string MakeReadOnlyIf(Func<bool> predicate);}
Our concrete implementation of our IControlForOptions for our TextBox looks like this:
public class TextBoxForOptions<VIEWMODEL, TProperty> : IControlForOptions where VIEWMODEL : class { private readonly Expression<Func<VIEWMODEL, TProperty>> _expression; private readonly HtmlHelper<VIEWMODEL> _helper; /// <summary> /// Initializes a new instance of the <see cref="TextBoxForOptions<VIEWMODEL, TProperty>"/> class. /// </summary> /// <param name="helper">The helper.</param> /// <param name="expression">The action we will use </param> public TextBoxForOptions(HtmlHelper<VIEWMODEL> helper, Expression<Func<VIEWMODEL, TProperty>> expression) { _helper = helper; _expression = expression; } /// <summary> /// Based upon our predicate condition we will either render out a textbox or a readonly textbox /// </summary> /// <param name="predicate">The condition we wish to test.</param> /// <returns>Either a normal textbox or an readonly textbox</returns> public string MakeReadOnlyIf(Func<bool> predicate) { var makeReadOnly = predicate(); if (makeReadOnly) { return _helper.TextBoxFor(_expression, new { ReadOnly = true }); } return _helper.TextBoxFor(_expression, null); } }
In this example I have created a fluent interface that can conditionally out put a Textbox which is readonly. Using this approach you could easily extend this to any HTML control and modify any attribute you wanted to.