I'm rendering a menu in my _Layout.cshtml file. One section of the menu should only render if the user is in the admin role. We are using custom roles in our DB. The Admin field comes back as a single character, "Y" or "N". This was originally a legacy application and it's being migrated to MVC 5. LINQ or Entity Framework is not being used.
I'm creating the menu as a partial view, Menu.cshtml:
<ul class="nav" ui-nav>
...
@if ((bool)ViewData["Admin"] == true)
{
<li>
<a href="#"><span>Users and Roles</span></a>
</li>
}
</ul>
My _Layout.cshtml file:
...
<aside id="aside" class="ui-aside">
@Html.Action("Menu")
</aside>
...
The controller's action method is where I'm having the problem. My intention was to return a boolean value and simply toggle the rendering of the protected part of the menu based on that value in the partial view. Right now it's rendering True or False in the browser where the menu should display, because that boolean value is being returned as a HTML string from the action method.
I know I could return that part of the menu as a string from the action method but I want to avoid this because there will be other sections of the menu rendered depending on role values. I don't want to end up with a slew of partial views. If possible I also want to avoid tag helpers. I simply want to toggle the rendering of the HTML menu section in the partial view based on the boolean value in the action method.
Action method:
[ChildActionOnly]
public bool Menu()
{
using (connection...)
{
...
object adminObject = command.ExecuteScalar();
if (adminObject != null)
{
string admin = adminObject.ToString();
if (admin == "Y")
ViewData["Admin"] = true;
else
ViewData["Admin"] = false;
}
}
return (bool)ViewData["Admin"];
}
Your partial view can declare a model that goes with it. Instead of using @Html.Action()
to return the result as HTML string, you can use @Html.RenderAction()
to
In this way, your logic of how to get user role status is "encapsulated" in the child action method, and the logic of whether to show or hide the portion of the menu is "encapsulated" in the partial view.
In your menu where you want to render the portion only available for admins, you use @Html.RenderAction()
to execute buildUsersAndRoles
action in menu
controller (I just made them up):
<ul class="nav" ui-nav>
...
@Html.RenderAction("buildUsersAndRoles", "menu", new { area = "" })
...
</ul>
The basic idea is to render your logic to fetch necessary data you need, including the flag to show whether the user is admin or not. And based on those, you can determine what you want to pass back inside the view model.
My approach here is to pass NULL if the user is not admin since there is nothing to display, and pass the view model that contains users and roles information otherwise:
public class MenuController : Controller
{
...
[ChildActionOnly]
public ActionResult BuildUsersAndRoles()
{
using (connection...)
{
...
object adminObject = command.ExecuteScalar();
if (adminObject != null)
{
string admin = adminObject.ToString();
if (admin == "Y")
{
var vm = new UsersAndRolesViewModel
{
Users = new List<...>(),
Roles = new List<...>(),
...
};
return PartialView("_UsersAndRolesPartial", vm);
}
}
return PartialView("_UsersAndRolesPartial");
}
}
...
}
Finally, in the portion you only want to display when the user has admin role, you have a view model to work with:
@model ...UsersAndRolesViewModel
@if (Model != null)
{
<h5>Users and Roles</h5>
...
}