MVC Widgets with RenderAction

Last year when MVC was still in CTP, I blogged about using MVC ComponentController and ViewUserControls to render widgets.  When MVC CTP4 was released, the ComponentController was eliminated from the framework and the RenderAction() helper method was introduced.  Rob Conery has a good post that discussed that here.  Despite the ComponentController being eliminated from the framework, I still frequently get questions about the older blog post as well as asking for the code sample to work with the current release of MVC.  I've updated the code to use the RenderAction() helper method and the new full code sample can be downloaded here.

But first, a couple of points.  By the time MVC was released the RenderAction method was moved to the MVC futures assembly.  The futures assembly can be download from CodePlex along with the rest of the MVC source here. Also, it is important to note that the RenderAction method is not without its share of controversy.  The argument against RenderAction is that it violates the separation of concerns that MVC is supposed to enforce – specifically that the views should have no knowledge of the controllers.  The argument on the other side is that sometimes breaking this rule is "pragmatic" and the views often have to know about controllers at least enough to be able to construct ActionLinks, etc. so this is not much different.  Both good arguments but from a "purist" point of view, it is definitely desirable to keep your views as lean as possible and, in general, not have knowledge of controllers.

So revisiting the so-called "widget" example from last year's post, let's say you have a containing page where you want to have a bunch of widgets.  One way to render a widget would be to use RenderUserControl like this:

<%=Html.RenderUserControl("~/Views/Home/AddressEditor.ascx", new AddressViewData(ViewData.Model.Contact.HomeAddress))%>

With this method you have to pass in the model from the containing page.  But if your containing page has 10 widgets then it's not going to be very pretty to pass in 10 models to these various views.  And if they're widgets, you want the logic more self contained for each widget – not just all on the parent containing page. In my previous post I showed calling a ComponentController's action method via javascript like this:

<script type="text/javascript">
    $(function() {
        $('#secondWidget').load('/Widget/Widget2');
        $('#thirdWidget').load('/Widget/Widget3');
    });
</script>
 
<div id="secondWidget" class="widget"></div>
<div id="thirdWidget" class="widget"></div>

This still works but since the ComponentController is gone, these just point normal Action methods.  The other way to do this is the use RenderAction like this:

<div id="firstWidget" class="widget">
    <%Html.RenderAction<MvcWidget.Controllers.WidgetController>(c => c.Widget1()); %>
</div>

So, while this works just fine, you can now see concretely that the view has knowledge of the controller.  The upside is that the logic for passing the model to the view is encapsulated in the WidgetController rather than adding that responsibility to the containing page.  So, while not perfect, this is one way it can be handled in MVC. The complete code sample can be downloaded here.

Tweet Post Share Update Email RSS