ASP.NET MVC options for consolidating HTML

It comes up very early in any ASP.NET MVC application: duplicated HTML.  Bits and pieces of final rendered page may appear on two or more different pages, and that’s duplication we would like to eliminate.  In WebForms land, we had quite a few options to consolidate and refactor that common HTML: Master Pages, custom controls, user controls, composite controls and so on.  In ASP.NET MVC, the potential for duplicate HTML is still close to a guarantee, so we’ll need to explore different options for common HTML.  This time around, our options are a little more straightforward than in WebForms land.

Off the top of my head, we have at least five options for consolidating HTML:

  • Master Pages
  • Partial
  • RenderAction
  • PartialRequests/Subcontroller
  • Extending HtmlHelper (or similar)

Each of these has its plusses and minuses, and each has a specific scenarios where they work best.  When it comes to the point where we need to consolidate HTML, it’s important to understand the strengths of each solution, so let’s go through each and examine them carefully.

Option 1: Master Pages

In ASP.NET MVC, we have exactly the same options for organizing HTML as we did in WebForms, that is, Pages (.aspx), Master Pages (.master) and User Controls (.ascx).  However, unlike WebForms, none of these has any code associated with them, they are merely a way to organize views.  They don’t handle requests, they just render a model.  As such, we can move a lot of the common layout HTML, common scripts, CSS, etc.

Strengths

  • Individual ViewPages (.aspx) can define their Master Page, as in WebForms
  • ViewPages decide what their Master Page is
  • Render information that doesn’t need input from a Controller

Weaknesses

  • Controllers have no knowledge of Master Pages (nor should they)
  • Master Pages can’t really use any View Data (nor should they)

When to use it

Master Pages are great for common HTML that doesn’t need any direction from a Controller for what to do or what data to show.  A Master Page is strictly a View concern, for consolidating HTML that the Controller does not care about.  For example, we wouldn’t want a Controller to pass down anything in a ViewModel that a Master Page would use, because, at any time, the View could decide it needs to use a different Master Page for whatever reason.  This isn’t obvious from the Controller side, so we don’t really want to rely on it.  There are special cases to this rule, with nested Master Pages, dynamic components and so on, but it’s just not nearly as obvious or discoverable as other options.

Ideally, our Master Page would have next to zero code in it, used merely to create layouts for the actual Views being rendered.  The Master Page would have zero or very, very little input from the Controller, ideally zero, just to keep our concerns separate and our architecture sound.  Master pages should be strictly used for common layouts, and nothing more.

Option 2: Partials

Partials are what I consider the “Extract Method” refactoring of a View.  We see common HTML in many Views, each using the same ViewModel type to render the same HTML.  When we see the same HTML working off of the same ViewModel (or one that looks the same and we can consolidate), we can extract that common HTML to a Partial, and use the RenderPartial method in our View to render this common HTML from many different Views.

Strengths

  • Testable, as you can make sure the Controller passes the right ViewModel to the View
  • Can show dynamic data, as the Controller controls what ViewModel data the Partial gets
  • Nestable and composable as much as a ViewModel can be
  • Partial Views aren’t tied to any parent View that uses them

Weaknesses

  • Controllers must be aware of all Partials used – as they must supply the data in the right format
  • Because Controllers supply the data, disparate Controllers become implicitly bound by the Partial’s ViewModel they need
  • A Partial’s ViewModel is often supplied through ActionFilters, which works, but can be a little too “behind the scenes voodoo” for some

When to use it

The big choice for going with a Partial is deciding who is going to responsible for supplying an HTML snippet its ViewModel.  If you want the top-level Controller supplying the ViewModel data, explicitly in the Action or implicitly in an ActionFilter, then a Partial is the way to go.  Partial are easily testable, as you can directly query the ViewModel that the Action provides.  Even with an ActionFilter, you can independently test both the ActionFilter and make sure it’s applied to the right Controllers and Actions through reflection.  Where Partials become troublesome is where you have widgets, banners or other common areas in your screen that have really nothing at all to do with what your action is doing and are not part of the logical whole of a View.

Option 3: RenderAction

In ASP.NET MVC, we have the option of creating a “mini-pipeline” from a View through RenderAction.  With RenderAction, you’ll supply the Controller, Action, and any routing information from inside a view, allowing for complete separation of the parent Controller being responsible for supplying a child ViewModel.  A full-blown Controller executes from a RenderAction call, rendering its own View, its own ViewModel (however it decided to build it), and any other child Partials or the like.  Underneath the covers, RenderAction constructs an entirely new RequestContext, allowing the child Controller to have its own ActionFilters and anything else a Controller might want.

Strengths

  • Parent Controllers do not have to populate multiple disparate child View widgets with their ViewData
  • No performance penalty, as these internal micro-requests are cheap to create
  • Individual child controllers are testable

Weaknesses

  • RenderAction is in the View, which makes it immediately harder to test
  • Because nothing in the Controller indicates there is a shared widget component, it’s not quite as obvious
  • Care must be taken to organize these widget Controllers away from regular ones, as you don’t often want them to be publicly accessible

When to use it

For those that believe RenderAction in a View violates some kind of separation of concerns, in Steve Sanderson’s words, “just bite them on the face immediately”.  As he points out, internal subrequests are a natural phenomena in HTML and today’s web.  AJAX, IMG, CSS links all are subrequests for additional pieces of a View.  RenderAction is perfect for cases where you have independent widgets on a screen, and are not part of a logical whole.  That Login component at the top?  Perfect for RenderAction.  Putting a Login component as a Partial just puts more moving parts in your main MVC pipeline.

Bottom line, RenderAction promotes separation of concerns when applied properly.

Option 4: Partial Requests/Subcontroller

Partial Requests came from Steve Sanderson, and the Subcontroller is part of MVCContrib.  Recognizing the “testable” issue of RenderAction, both of these pass…something to the View that represents a complete request.  Because the request container object is passed to the View through ViewData, it becomes inherently testable.  The final call in the View looks very similar to a RenderAction, except it operates off of a well-known location in the View.

Strengths

  • Provides a testable version of RenderAction
  • Puts the responsibility of choosing the child Controller/Action on the parent Controller instead of the View
  • Supports AJAX easily (as individual pieces can handle requests)

Weaknesses

  • More moving parts than either RenderAction or Partials
  • Parent Controller again responsible for setting up child widgets, in an Action Filter or in an Action

When to use it

Partial Requests and Subcontrollers are both basically variations on a theme – passing something that represents a request down to a View, giving control back to the Controller on how widgets are rendered.  Since both Partial Requests and Subcontrollers resolve to Controllers, they can have dependencies, rely on services, use model binding, contain Action Filters, and just about everything else interesting dealing with a Controller.  They can even be instantiated through your regular Controller factory (as long as you plug the correct context items in).  If testability is a top concern, these options will work well as they combine the power of RenderAction with the testability of Partials.  One piece that still isn’t testable is whether or not the View does anything with the request model passed down to it.  Of course, this is an issue with anything passed to the View, you’ll still probably need some automated UI tests in place to cover all of your bases.

In many cases, Partial Requests are more than you need, and just a straight up RenderAction will work just fine.  If it’s some kind of purity issue with the View deciding what to render…bollocks!  Don’t pick this option if you want to remain “pure”, pick it because it gives you an edge in testability and a little more separation of concerns on the View side.  Since the View can’t decide what widget to render in a RenderAction call, the parent Controller can take more liberty in the request model passed down, so you’ll get more control of widgets with this option.

Option 5: Extending HtmlHelper (or similar)

On the View side, ASP.NET has what I see as micro-code generation, to help generate individual HTML elements in a type-safe manner.  Input elements, links, forms and so on are often easier to create with a helper method to generate them.  You don’t have to use the built-in HtmlHelper methods, defining extension methods allow generation of new or specialized elements.  In the case of CodeCampServer (and our current production applications), we have intelligent input builders that reflect over an Expression, looking for property types, attributes, and other information to select an input builder to output completely consistent form elements, site-wide.

Strengths

  • All the necessary pieces to create small fragments of HTML
  • Easy to extend and add new functionality
  • Small footprint for extensions
  • Individually testable

Weaknesses

  • Can get complicated, quickly
  • Not suited for anything more than an HTML element or two
  • Restricted to what you would want to do in one method

When to use it

Creating little helpers are great for creating little bits of HTML, where a Partial is too much.  If you have a common small snippet of HTML fragments, where you want to enforce a convention on how that snippet is generated, HtmlHelper extensions are perfect.  You’re not limited to HtmlHelper, as you can create base View classes to attach different HTML builders.  If you try to get too clever with micro-generation, it can come back to bite you, so keep an eye on when you should move up to one of the more appropriate View “widget” generators, such as Partials or RenderAction.  Generally, if you’re generating three or fewer HTML elements in one helper extension, you’re in the right ballpark.

Wrapping it up

One thing to keep in mind is that all of these options covered here are just that, options.  Each is appropriate in their own scenarios, and there is some overlap between them.  But having all the options available gives you the choice to pick the right one for each scenario.  In one recent project, we’ve used all of these options all over the place, because each has their own strengths and weaknesses.  By using each option in the sweet spot they were designed for, we’re able to grow our application without succumbing to the complexity of duplication or the complexity of eliminating duplication incorrectly.

Related Articles:

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

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in ASP.NET MVC. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • brad

    is it just me, or do all of these poor options make me miss controls?

  • http://www.tobinharris.com Tobin Harris

    I wonder if the MVC team had consider adding the ViewHelpers which Rails has – this is the default way to simplify view code (see http://shrinkify.com/ied).

    “Controllers must be aware of all Partials used – as they must supply the data in the right format”.

    Can you point to an example of this, I didn’t realize that was the case?

  • http://blog.brownie.com.au Andrew Browne

    Just to be sure. Its okay for a MasterPage to have RenderActions so long as they don’t have code/depend on viewdata themselves?

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

    @brad

    These options are poor? I can’t say I miss controls…all of these options are decidedly less complex than all of that ViewState hooey. With MVC, because of the architecture, I have many more appropriate options available than having to design a heavy-weight control.

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

    @Tobin

    Every Controller needs to know what all of the Partials need for their ViewModel. If you have widely-used Partials, every Controller needs to understand that abstraction, and compose its ViewModel appropriately.

    Two different Views using the same Partial – now anything using that View needs to understand that there is a componentization underneath, and form its ViewModel appropriately.

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

    @Andrew

    Yeah, RenderAction is fine, as it doesn’t require ViewData. It just seems inappropriate for a Controller to be aware of how the Views are structured.

  • Almond

    This is a great summray of a issue I bumped into when I first started using MVC and still see a lot of other people posting online about.

    Thanks for your great article!

  • brad

    @bogardj
    for starters, i am not suggesting returning to viewstate and heavy controls, i just dont like the way all these options “feel”. I think my biggest problem is the fact that you do the data mapping out to html on the view side and then back in on the controller side. This doesnt pass the smell test. I have not fully come up w/ a solution, yet so i guess i shouldnt bitch too loudly ;)

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

    @brad

    I’d love to see some code on the problems you’re seeing. If you get a chance, put a link back here to a blog post with the issues you’ve described.

  • http://johnwest.spaces.live.com John

    All of these are ways to encapsulate HTML and view-creating code for reuse. They all have plusses and minuses. They require understanding interactions between parts. Sounds a lot like webform usercontrols to me. You’re replacing page lifecycle and viewstate with other pieces that are also complex.

    This whole argument reminds me of the C++ vs. C# argument. It’s very much the same. C++ people want maximum flexibility and power. C# people are willing to give some of that up for higher-level coding with strong types. Neither is wrong. Both have their places. Same with Webforms vs. MVC. Both are smelly in their own ways. Both make some things harder and some things easier. A person has to decide whether they want to do low-level programming (html, javascript), etc., vs. higher-level programming (webforms, controls), etc. With lower-level programming, you can always build reusable code to give you a higher-level feel, and with higher-level programming, you can also make direct calls to lower-level code when you need to.

    Me, I’m sticking with webforms until someone can tell me a better way. So far, at least with this version, MVC isn’t it. I’ve been doing web development since the original ASP. I did dhtml/xmlhttprequests back in 99 with IE before it had a name. I celebrated being able to get away from direct HTML and the like. Without a compelling reason, I don’t want to go back. And for me, better testability is not enough. I’ll take easier front-end coding with harder testability vs. harder front-end coding and easier testability any day.

    Just my thoughts.

    John West

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

    @John

    Fair enough, but I’m dealing with scenarios where I don’t want to abstract HTML. HTML + JavaScript + jQuery are easy. WebForms solved a problem I didn’t have, and the PostBack model is only ideal for very targeted scenarios, such as things like portals.

    Living in MVC land (not just ASP.NET MVC, but MVC in general) has let me focus on the _real_ problems of building an application, not contrived ones like ViewState, page lifecycle and all the heavyweight cruft of Controls.

    For example, a “for” loop for creating

  • tags is easier than a Repeater, in my experience. It’s obvious and simple to explain and use. In my experience, it’s much harder to explain the WebForms model than MVC.
  • http://blog.vuscode.com Nikola Malovic

    I wonder whe Silverlight plugin would be on 99% of browsers, would this MVS vs WebForms debate still be of any importance

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

    @Nikola

    I doubt it. Silverlight isn’t for web applications. It’s for WPF applications hosted on the web. That’s a _huge_ difference in experience for the end user. I think we all need to abandon the “Flash/Silverlight/Javaservlets will replace the web” idea, they don’t and never will.

  • http://johnwest.spaces.live.com John

    True, a simple LI might be easier with a for loop. But you can still do that directly on a webform if you watn. But if you want to have a reusable list that can be customizable via parameters, then all of a sudden it’s easier to create a user control.

    For me, with writing LOB applications, or even ecommerce apps, a lot of the problem stems around keeping parameters during postback and using a control multiple times on a page while ensuring that all of the ids are unique. Stuff like that is a cinch with webforms, while it requires hacks that, to me, are just as bad as what people complain about regarding lifecycle.

    To each his own. I want to believe in MVC. Maybe in a couple of releases many of my issues will be resolved or mitigated. By then, though, Scottgu and team might have made webforms much more workable, too.

    It really is fun. Our career path will never be boring :) .

  • Mike

    For me the best thing about MVC is that I don’t have to deal with the nutty page/control lifecycles any more. Good riddance!!

  • Another Mike

    Instead of passing an object to the view and doing a for each loop I am just generating the html for the table in the model. Then passing the html / string to the view. Is this bad design?