Use a shared controller class for entire area?

341

Solution 1

For something like a menu that should be repeated throughout a particular area, you want to use a child action:

[ChildActionOnly]
public ActionResult AdminMenu()
{
    var menu = _db.Menus.Include("MenuItems").First(m => m.Name == "Admin menu");
    var model = new MenuModelView
    {
        Name = menu.Name,
        CssClass = menu.CssClass,
        CssId = menu.CssId,
        Deleted = menu.Deleted,
        MenuItems = menu.MenuItems
    };

    return PartialView("_AdminMenu", model);
}

I changed your code around a bit so that you actually pass your view model to the view instead of stuffing it in ViewBag. Don't use ViewBag. It's awful.

_AdminMenu.cshtml

@model Namespace.To.MenuModelView

<!-- render your menu and just your menu here -->

Areas\Admin\Views\Shared\_Layout.cshtml

...

<!-- place this where you want the menu to appear in your layout -->
@Html.Action("AdminMenu", "ControllerNameWhereChildActionExists")

...

Solution 2

Yes, you can... you can use inheritance to create what is effectively a shared controller class. Like so...

Public Class AdminBaseController : Controller {

    public ActionResult Index()
        {
            var model = _db.Menus.Include("MenuItems").First(m => m.Name == "Admin menu");
            ViewBag.AdminMenu = new MenuModelView
            {
                Name = model.Name,
                CssClass = model.CssClass,
                CssId = model.CssId,
                Deleted = model.Deleted,
                MenuItems = model.MenuItems
            };

            return View();
        }
    }

Then you can build all of your other controllers to inherit it, like so:

Public Class AdminController1 : AdminBaseController
Share:
341

Related videos on Youtube

Floris
Author by

Floris

Updated on November 27, 2022

Comments

  • Floris
    Floris over 1 year

    I've got a area "Admin" on my mvc application with for each page it's own controller. Like PageController and ModuleController etc. On my shared layout file (View/_Shared/_LayoutAdmin.chtml) the menu gets loaded dynamicly into a @model var. Now in my HomeController I have this method the fetch the menu:

        public ActionResult Index()
        {
            var model = _db.Menus.Include("MenuItems").First(m => m.Name == "Admin menu");
            ViewBag.AdminMenu = new MenuModelView
            {
                Name = model.Name,
                CssClass = model.CssClass,
                CssId = model.CssId,
                Deleted = model.Deleted,
                MenuItems = model.MenuItems
            };
    
            return View();
        }
    

    This block of code is needed on every controller class. I don't think duplicating this code is the best idea. So I was wondering if it is possible to have this method in a shared controller class for the entire Admin area. Like a code behind class for the Shared/_LayoutAdmin.chtml file.

  • Floris
    Floris over 9 years
    This works, thanks. I ended up fetching the menu from the constructor in the AdminBaseController class.
  • Chris Pratt
    Chris Pratt over 9 years
    While this is a perfectly valid answer, it's not ideal to rely on all your controllers inheriting from a base controller in order for something like a menu to show on the page. If you forget or a new developer comes along that doesn't know that every controller must inherit from this base controller, then all of a sudden the menu is gone with no obvious reason why. Using a child action is preferable as it just works, no matter what your controller inherits from.
  • Floris
    Floris over 9 years
    But this child action needs to be copied to all controllers within the admin area. That's a lot of duplicate code, not sure if that's what we want.
  • Chris Pratt
    Chris Pratt over 9 years
    Not at all. You just need to put it in one controller, and the call that partucular action and controller in your layout. No duplicate code at all.