tags:

views:

309

answers:

3

I'm having trouble determining where to place navigation for an MVC app. For example, say you have the following structure:

  • Conferences
    • South Eastern Conference
      • Florida Gators
      • Georgia Bulldogs
      • Arkansas Razorbacks
    • Pac-10
      • USC
      • Hawaii
    • Big East etc...

How would you best create a structure for implementing a 'main' navigation and subsequent 'sub' navigation? Using the hypothetical example, You'd have specific sub navigation for each conference, showing its respective colleges (and only that conferences colleges).

Is this something you'd handle in the main view and just hide the non-selected conference? Or would you create a menu helper (or yet another partial) and call that from each individual college's view?

+1  A: 

Best way is to use multiple, nested master pages. e.g. Site.master would contain your top-level nav (list of conferences?) then you'd have a different master page for each conference that would 'extend' site.master. You can, in theory, have as many nested master pages as you want. Finally, Florida Gators etc would be 'real' views (i.e. non-master pages).

The tricky part is telling any parent master page which navigation item is currently selected. Because you can't bind master pages to the ViewModel you'll have to use the View Dictionary e.g. View["SelectedMainNavItem"].

Chris Arnold
Although this is an intention of nested master pages, I personally think this seems like an ugly way of handling navigation.
MunkiPhD
+1  A: 

Why not use some global layout template that always displays the main navigation, and relies on some helper to render the subnav? (The helper may be superfluous -- you might just output the subnavigation inline in the layout template)

Your controller passes current category/sub-category, and some data structure describing the current subnavigation options, to the view.

timdev
This is inline with what I was aiming for, although there seems to be some limitations to it, such as whether to render partials/use helpers
MunkiPhD
I disagree Tim. Your controller should have no knowledge of how your view decides to render itself. The controller is solely responsible for gathering data from the model and then presenting it to a selected view - nothing more.
Chris Arnold
A: 

After contemplating this issue for a while along with the suggestions, I came up with this solution. Since my subnavigation will always be below the main navigation, I decided to go with the Convention over Configuration method.

In my Site.Master, I have the following two render partials. One displays the main navigation and the other makes a call to BuildSubNavigation to display get the name of a partial to render:

<% Html.RenderPartial("_MainNavigation"); %>
<% var submenu = ViewContext.BuildSubNavigation();
    if (submenu != null) {
          Html.RenderPartial(submenu);
}%>

Granted, this could be thrown into a Helper, and I intend to do that, this is more explicit and aids in the understanding of the issue.

What this does is call the BuildSubNavigation method. It goes with the convention that if a controller is to have a specific sub navigation, there will be a partial in the form of "_Navigation" So in the spirit of the example, one partial would be "_SouthEasternConferenceNavigation" What I do is then check to see if the current view actually exists. If it does, I return the name, where it's then used to render the partial.

public static string BuildSubNavigation(this ViewContext vc) {
    var controller = vc.RouteData.Values["controller"] ?? "";
    var viewName = "_" + controller + "Navigation";

    if (ViewExists(vc.Controller.ControllerContext, viewName, null)) {
        return viewName;
    } else {
        return null;
    }
}

And this is the method that checks whether the View actually exists against the current View Engine:

public static bool ViewExists(ControllerContext cc, string viewName, string masterName) {
    if (ViewEngines.Engines.FindView(cc, viewName, masterName).View != null) {
        return true;
    } else { return false; }
}

I'm unsure if this is the best way to do this, but it's working rather well for a small project I'm currently working on.

Thanks for the answers!

MunkiPhD