andrey.shedko
andrey.shedko

Reputation: 3238

ASP.NET MVC How to use PartialView in Layout?

I need to render menu in Layout by using partial view (if there is better approach, please let me know). I'm doing it this way (in Layout):

 @if (User.IsInRole("Admin"))
   {
      @Html.Partial("AdminMenu")
   }

And this how I call this in Controller:

public ActionResult AdminMenu()
  {
      var am = _amr.GetAdminMenu();
      return PartialView(am);
  }

So here is my partial view:

@model IEnumerable<DigitalHubOnlineStore.ViewModels.AdminMenuViewModel>

<div class="dropdown">
    <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
        Admin menu
        <span class="caret"></span>
    </button>
    <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
        @foreach (var item in Model)
        {
            <li><a href="@Html.DisplayFor(modelItem => item.MenuItemUrl)">@Html.DisplayFor(modelItem => item.MenuItemName)</a></li>
        }
    </ul>
</div>

Unfortunately it's not working.

Upvotes: 3

Views: 30029

Answers (2)

Ross Bush
Ross Bush

Reputation: 15175

You either have to return the menu collection in the parent view, sending in the pertinent model to the Partial, or make another HtmlForm, AjaxForm or ajax call from your partial.

@Html.Partial("Partials/AdminMenu",Model.AdminMenuItems)

OR

//SERVER
  public ActionResult AdminMenu()
  {
      var am = _amr.GetAdminMenu();
      return Json(am,JsonBehaviour.AllowGet);
  }



  //CLIENT
    @using (Ajax.BeginForm("AdminMenu","AdminController", null,  
        new AjaxOptions
        {
            OnSuccess = "renderSuccess",
            OnFailure = "renderFailure",
            OnBegin = "renderBegin"
        },
        new
        {
            id = "frmViewerAdminMenu",
            name = "frmViewerAdminMenu"
        })
    )
    {
    ...

        <script type="text/javascript">
            function renderSuccess(ajaxContext){
               /// ajaxContext is whatever comes back from GetAdminMenu()
            }
        </script>
     ...
    }

OR

--Server Render View

public ActionResult AdminMenuRenderView()
{
    return Partial("AdminMenuPartial",GetAdminMenuItems());
}

--Partial AdminMenuViewDynamicLoader

 @using (Ajax.BeginForm("AdminMenuRenderView","AdminController", null,  
    new AjaxOptions
    {
        OnSuccess = "renderSuccess",
        OnFailure = "renderFailure",
        OnBegin = "renderBegin"
    },
    new
    {
        id = "frmViewerAdminMenu",
        name = "frmViewerAdminMenu"
    })
)
{
...
    <div id="divAdminMenuContent"></div>

    <script type="text/javascript">
        function renderSuccess(ajaxContext){
            $('#divAdminMenuContent').html(ajaxContent);
        } 
    </script>
 ...
}

--Partial AdminMenuPartial

@model IEnumerable<DigitalHubOnlineStore.ViewModels.AdminMenuViewModel>

<div class="dropdown">
    <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
        Admin menu
        <span class="caret"></span>
    </button>
    <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
        @foreach (var item in Model)
        {
            <li><a href="@Html.DisplayFor(modelItem => item.MenuItemUrl)">@Html.DisplayFor(modelItem => item.MenuItemName)</a></li>
        }
    </ul>
</div>

Upvotes: 3

Ryan Mann
Ryan Mann

Reputation: 5357

You need to use

@{Html.RenderPartial("Admin");}

Html.Partial only returns the view as a string, it does not write it to the response, so just calling it does nothing, the returned string falls out of scope. The reason for it's existince is so you can render a partial view to a string and then insert the string in multiple places without having to render the partial view multiple times. For example if you want the same paging markup to be at the top and bottom of the page. Or maybe you want to duplicate a Tab Partial view to create multiple tabs, etc etc etc.

To get your current code to work, try this

Model ......
@{
   Layout=.....
   var adminMenu = Html.Partial("Admin");
}
@if (User.IsInRole("Admin"))
{
   Html.Raw(adminMenu);
}

Html.RenderPartial does the same thing but internally calls Write to write it to the response.

Now if your Partial Is Accepting a model then you need to give it a controller method,

[ChildActionOnly]
public ActionResult AdminMenu(AdminMenuViewModel model)
{
    return Partial(model);
}

Then update your calling code to,

 @{Html.RenderAction("AdminMenu", "ControllerHere", new { model = TheAdminViewModelHere });}

Personally I pefer using ChildOnlyAction's for my partial views because I can move all the processing logic into the controller for the partial view, which performs better. It also allows a partial view to know things about it's parent context as it gives you access to the controller to add information to the model to pass to the partial view.

Upvotes: 5

Related Questions