views:

247

answers:

3

I've been working a lot with asp.net web forms and one think that I like about the is the consistency with the generated markup e.g. if you create a composite control for a TextField you can control the generated markup in a single class like and don't break the SRP:

<form:textfield id="firstName" runat="server" required="true" label="First Name" />

I you're your going to generate the markup by hand it might look like this:

<label for="firstName" id="lbl_firstName">Name <span class="required">*</span></label>
<input id="firstName" name="firstName" type="text" value="" />

The problem is when would like to change something for example add a wrapping div or move the span. In worst case you have to edit thousands of views.

That's why I really like the MVC Contrib FluentHtml.

<%= this.TextBox(x => x.Message.PostedBy).Class("required").Label("Name") %>

My question is what do you think is the best way to add a wrapping div for the code line above? I think hand writing is not an option because of the arguments above? Perhaps extending the TextBox : MvcContrib.FluentHtml.Elements.TextInput?

+2  A: 

have you checked InputBuilder in MvcContrib project? it is used in Codecampserver as well. have a look and i think u will like it.

ashik_gurung
The input builders are pretty nice and is what I'm looking for:http://github.com/mvccontrib/MvcContrib/tree/master/src/Samples/MvcContrib.Samples.InputBuilders/http://www.lostechies.com/blogs/hex/archive/2009/06/09/opinionated-input-builders-for-asp-net-mvc-using-partials-part-i.aspxI cannot manage to get the input builders and FluentHtml to play well together, I don't know if it's possible at the moment.Another option is to add an extension method to FluentHtml and that will do the trick as well.
orjan
+1  A: 

Honestly, I don't think the example case you've given applies to real world. A textbox is a textbox. If you need one, you render one.
If you need a more "complex" control like a textbox wrapped in a div tag, then you can have a partial view for that.

For example, Model :

public class CustomControlModel {
    public string Name { get; set; }
    public string Value { get; set; }
    public string Class { get; set; }
    public bool WrapInDivTag { get; set; }
    //you get the idea
}

Custom Control :

<%@ Control Inherits="System.Web.Mvc.ViewUserControl<CustomControlModel>" %>
<%if (Model.WrapInDivTag) {%> <div> <% } %>
    <%=Html.TextBox(Model.Name, Model.Value, new { @class = Model.Class })%>
<%if (Model.WrapInDivTag) {%> </div> <% } %>

And when rendering :

<%Html.RenderPartial("CustomControl", 
    new CustomControlModel { Name = "name", WrapInDivTag = true }); %>

That's a very simple example but I hope it explains why I suggested partial views. Don't forget that you can expose another property to get which tag to render etc.

çağdaş
Actually I don't agree with you, the example is simple but it still works. There's a reason for that the Fluent TextBox is rendering a label as well so you don't have to write i by hand. For exactly the same reason you might want to add a wrapping div and control the output from a single location.
orjan
@orjan, If that method `TextBox` renders a `<intput type="text" />` **and** a `<label />` tag, that's because they are related to each other. But if you want to render an input, label **and** a div tag, then I have to think it's not a simple textbox anymore, but a custom control.
çağdaş
@çağdaş, I agree that the input and the label are related to each other but they also "need" some kind of wrapping element <div>, <li>, <p> to be cross browsers CSS stylable, that's why the composite controls are great. Creating a partial view for every input field sounds like a great deal of overhead to me and the code won't look tidy.
orjan
Actually, you don't need to create a partial view for every input. Partial views can have models too. I'll edit the answer to give an example...
çağdaş
I agree with orjan, the code example orjan gives is concise and reusable. Partial Views are for larger components.
Chuck Conway
A: 

InputBuilders are one option. With FluentHtml you could create a custom element, something like this:

public class TextBoxInContainer : TextInput<TextBox>
{
    public TextBoxInContainer (string name) : base(HtmlInputType.Text, name) { }

    public TextBoxInContainer (string name, MemberExpression forMember, IEnumerable<IBehaviorMarker> behaviors) : base(HtmlInputType.Text, name, forMember, behaviors) { }

    protected override ToString()
    {
         divBuilder = new TagBuilder(HtmlTag.Div);
         divBuilder.InnerHtml = ToString();
         return divBuilder.ToString(TagRenderMode.SelfClosing);
    }
}

To use this from your view you would extend IViewModelContainer something like this:

public static MyTextBox TextBoxInContainer <T>(this IViewModelContainer<T> view, Expression<Func<T, object>> expression) where T : class
{
    return new TextBoxInContainer (expression.GetNameFor(view), expression.GetMemberExpression(), view.Behaviors)
     .Value(expression.GetValueFrom(view.ViewModel));
}

Then if you want to change your container to a span sitewide, you change the ToString method of TextBoxInContainer.

Tim Scott