views:

419

answers:

2

Hi there,

The more I use ASP.NET MVC, the more I love it. However, in the case of showing model data on master pages there seems several ways of doing it. I am unsure as to the best solution.

My example would be a commerce site where I want to output a list of product categories on every page and also show the status of the visitors cart.

In asp.net web forms I would typically do this using user controls each doing their own databinding to retrieve the required data.

In MVC all data should be passed by the controller.

So regarding the categories the simplest solution would seem to be to pass this in View data in the controller action:

ViewData["Categories"] = _service.GetCategories();

However, doing this for every action isn't very DRY so following this article I created a base controller that adds the required data to my ViewData:

    public class AppController : Controller
{
    IAppService _service;

    public AppController() { }
    public AppController(IAppService appService)
    {
        _service = appService;
        SetSiteData();
    }

    private void SetSiteData()
    {
        ViewData["Categories"] = _service.GetCategories();
    }
}

I then created an extension for ViewMasterPage:

        public static void RenderCategoryList(this ViewMasterPage pg) {
        pg.Html.RenderPartial("CategoryList", pg.ViewData["Categories"]);
    }

And in my MasterPage:

        <div>
        <%this.RenderCategoryList(); %>
    </div>

This seems quite a clean approach. However, is this the best way as I have also seen suggestions of creating a ViewModel for your MasterPage. I could see that perhaps as your ViewModel data grows, this may be a better solution.

Regarding the cart status, I suppose I would do something similar but am unsure whether RenderAction would be more appropriate (When to use RenderAction vs RenderPartial with ASP.NET MVC). Thanks, Ben

+2  A: 

That works, although it's not the way I would do it for 2 reasons:

  1. I don't like sticking data into ViewState since you essentially cast it as object
  2. By requiring a base controller you're limiting the functionality to controllers that inherit from this basecontroller (which might not be an issue though)

I think this would be a perfect use of RenderAction (part of the MvcFutures project). This helper can be used to render an Action on another controller. So you might have a ProductController with a ListCategories action, you could just do something like:

<% Html.RenderAction<ProductController>(x => x.ListCategories()); %>

ListCategories would call

_service.GetCategories();

and might stick the info into its own Model. Then it would pass that model to the View would would be a Partial Page.

statichippo
+1 for RenderAction, it's almost a perfect solution, after MVC2 comes out and you can cache the RenderAction results it will be perfect.
mxmissile
Do I need to do anything to use the strongly typed RenderAction call you are using above? I'm using mvc 2.0 preview and don't seem to be able to construct the RenderAction method in this way.
Ben
A: 

Thank you - RenderAction was perfect the job.

I got more information from here.

So for the benefit of others, here is an example of how I am outputting cart status:

Action:

    [ChildActionOnly]
    public ActionResult CartStatus()
    {
        return PartialView(_service.GetCartSummary());
    }

Partial View (bound to Models.Cart)

<div class="cartSummary">
<%if (Model.HasItems) { %>
    Cart Items: <%=Model.Items.Count() %> | Total: <%=Model.TotalItems %>
<%} else {%>
    Your cart is empty. Please buy stuff!
<%} %>

Helper method for Master Page:

    public static void RenderCartStatus(this ViewMasterPage pg) {
        pg.Html.RenderAction("CartStatus", "Catalog", null);
    }

And finally master page:

<%this.RenderCartStatus(); %>

Thank you for the advice.

Ben
trying to implement this solutions but I cannot figure our where to create the helper method for the master page? Do I create another c# class file somewhere?
JBeckton
@JBeckon - yes you can create a static class to contain these extensions. Just be sure to reference the namespace in your view or in web.config.
Ben