Opinionated Input Builders for ASP.Net MVC using partials – Part 1

It has taken a while to really understand how different pieces and ideas can fit together to give a concise and productive form input helpers for the asp.net mvc framework. I have pulled together this idea from the following sources:

Now that I have given credit to everyone who has helped me get to the point of understanding how to pull of these pieces together.  

The Model comes first.

The goal of these control helpers is to reward you for developing MVC with the Model first.  Yeah there is a reason that Model View Controller starts with the Model. Using the strongly typed views in the aspx view engine we can carry the type down to the control helpers with intellisense and then build html input control based on conventions for rendering specific CLR types to specific HTML output.  Now my biggest problem with the ways that this has been attempted to date is that once helpers started to take on more mark up beyond the < input > tag it was hard to modify as that markup ended up being written in code rather than in a view file.  This is where I abstracted the mark up and the logic to decide which markup to render so that there is a solution that is easy to maintain the markup and it is easy to add new conventions or change the conventions for how a particular type or model property is rendered to a control.

First here is an example of a model rendered in a strongly typed view and the markup used to create this.  There are attributes applied to the model to add some specific control over how the UI is rendered. It is important to call out that in this case I have built a (view) Model specifically to represent a single view.  I do not intend to reuse this model in another views.  I think trying to get reuse out of models is a mistake in most circumstances, it is better to keep your model clean so that it represents exactly what you want for the View.  Each of these attributes are used by my conventions to decide which Partial View to render.

image

 

This is the example of the view page which is rendering the view and the markup used to create this.  The Html.Input uses the LamdaExpression syntax to declare which property needs and input. 

image

It is important to call out that the Guid property is rendered as a Hidden input tag and that is why it does not show up in the UI. This is a model element that is used to carry state in the form between posts.

Walkthrough of the Html.Input( ) method

The syntax for calling into the input method uses a lamda expression like so…..  This has full intelisense support.

image

 

The helper determines that since the Name is a string data type it decides by convention to render this property using the String.aspx partial view.

image

Here is the markup of the String.aspx partial view.  As you can see it uses a Master Page to control how its label and input is rendered.

image

Here is the Field.Master Master Page this controls the layout of the input, label, example text, and validation message.

 

 

 

 

 

 

image

Follow me on RSS and Twitter

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Eric Hexter

I am the CTO for QuarterSpot. I (co)Founded MvcContrib, Should, Solution Factory, and Pstrami open source projects. I have co-authored MVC 2 in Action, MVC3 in Action, and MVC 4 in Action. I co-founded online events like mvcConf, aspConf, and Community for MVC. I am also a Microsoft MVP in ASP.Net.
This entry was posted in .Net, Asp.Net, Asp.Net MVC, c#, CoC, mvccontrib. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Matt Hinze

    Eric it looks awesome. I love the templated partial views per input. That’s really important for a maintainable design. One though is that I do want the option to not use a view but to render a string or do something else. I don’t like the HtmlHelper at all, and I like to avoid it if possible. I’d stick to more like how we’ve done it, but with a default to the templated partial.

  • http://www.lostechies.com/members/erichexter/default.aspx erichexter

    @Matt Hinze,, I was really trying to avoid having a base class or using the this.Input syntax. That left me with making the entry point being a HtmlHelper extension method. The extension method is really just an entry point to get to the builder. it just instantiates the input builder.

  • Matt Hinze

    Just curious, why are you trying to avoid having a base class?

  • joe

    I really like the templated partials. Does the [Partial] attribute exist to render partial other than your specified by your conventions, or is it for something else? Also, is the source available somewhere?

  • http://www.lostechies.com/members/erichexter/default.aspx erichexter

    @Matt I am trying to avoid a base class because you only get one. The same way I do not want my persistence framework to force a base class on my domain model. I would hate to force a base class on my views because of one of my control rendering extensions.

  • http://www.lostechies.com/members/erichexter/default.aspx erichexter

    @joe source code to come soon…

  • Mihai

    I find the idea of having an attribute like [PartialView("")] on my domain model awful, I’d much rather configure the input builder externally.

    Anyways it’s an approach, but one that doesn’t fit my taste at all.

  • http://blog.maartenballiauw.be Maarten Balliauw

    I’m voting this for ASP.NET MVC 2.0 :-)

  • Stephen Young

    I thought one of the drivers behind MVC is to seperate your view from the model, you have just created a dependancy cycle between your view and your model.

    I think this defeats the whole point behind MVC. The approach you use is fine if it is used on a DTO (data transfer object) that is returned from the controller for use by a specific view. It should NEVER be used on your actual domain classes.

  • Andre Sachs

    Erm you have polluted your model with layout details, not a good start :)

  • http://www.lostechies.com/members/erichexter/default.aspx erichexter

    @Stephen Young. I totaly agree with you. I guess I did not make the opinion clear that the Model I am using here is specificly a View Model for one specific view. I have used a map to go from my domain to my view Model. I would never suggest putting any attributes from any framework on my domain model. I do not want any layer forcing that on the core of my system.

    I do have a way of implementing all of these attributes in the view by chaining methods on the Input( ) method itself. Sounds like I need to have a post.

  • Stephen Young

    @erichexter Excellent I would love to see how you do the chaining, I really think the basic concept is great as long as it is targeted at a view model. We do something very similar in our current architecture (although it has home spun MVC as we started before ASP.Net MVC was around)

    I have found in general unless you have a fairly trivial domain that doesn’t do much more than basic CRUD operations it becomes unwieldy to deal directly with a domain model inside your views. We have use case specific models (these usually map to a single view or a couple of closely related views) and map from the domain to the use case/view model.

    Regarding base classes, I share your aversion.

  • Duncan Godwin

    I like the idea. I’m wondering how far this could go towards Dynamic Data and display all fields using one method based on the view model. This would save adding a property to the view model and then updating the view. The form generating method could take some paramaterisation for the one input helper that is different.

  • http://www.lostechies.com/members/erichexter/default.aspx erichexter

    @Duncan, If you look at the source code I have a method call InputForm() which generates the entire form from a single method call. So I will have a post to show what that does. I did not want to start with that as it might take a lot of explaining of how all the magic took place, to get to the point where that was possible.

  • http://haacked.com/ Haacked

    Are you using the data annotations? Shouldn’t the LabelAttribute be DisplayAttribute?

  • 很少看到

    很少看到这样的文章
    把视图作为模型 进行高级抽象 是个好想法
    不过就像他们说的 还有待探讨

  • http://comments@5x1llz.com 5x1llz

    this sort of builds on Palermos example in MVC in action I like.. might leverage TextBoxFor instead though… and are there options to the attributes?

  • BorisCallens

    I like the idea.
    It would make sence to me to have constraints in my model. Constraints are not view dependant, they should always be there anyway.
    As long as you put the desired partial annotation in your DTO, I’m perfectly fine with it.
    I feel this has dftly potential.

  • http://webdevvote.com/MVC/Opinionated_Input_Builders_for_ASP_Net_MVC_using_partials_Part_1 WebDevVote.com

    Track back from WebDevVote.com

  • Saurabh

    Hi

    I am using this
    < %= Html.Input("Title",x=>x.Title) %>
    Html.Input doesn’t resolve.

    any idea? which assembly i am missing

    Kind Regards
    Saurabh

  • http://www.lostechies.com/members/erichexter/default.aspx erichexter

    @Saurabh Try the following … Add a reference to the InputBuilder project/assembly from your project and than add the < %@ Import Namespace="InputBuilder" %> tag in your view or in the web.config in your Views directory.

  • RassaR

    Hi

    I got model with list of ‘subject’ fields

    public IList subject { get; set; }

    I’m trying to create equivalent of


    etc

    How do i do that with input builder ?

    < %= Html.Input(c => c.subject[0]) %> does not work

    Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

    Line 127: public static object ValueFromModelProperty(PropertyInfo propertyInfo,object model)
    Line 128: {
    Line *129*: var value = propertyInfo.GetValue(model, new object[0]);

    Source File: C:\work\InputBuilder\DefaultConventions.cs Line: 129

  • http://www.lostechies.com/members/erichexter/default.aspx erichexter

    @RassaR the index logic can handle the arrays but the value logic needs to be changed to pull that value out properly. I will need to setup a spike to run through that example.

  • Gladys_yelland

    like it..