2

I am in the process of creating a prototype project using MVC 3 and I have come across a situation which I can't seem to find an answer for and it seems like I might be approaching the problem the wrong way.

A quick overview of my project; it is based on the MVC template you get with Visual Studio and I use the links (tabs) on the supplied _Layout view to access some of my other views. One of these links opens a second partial view that again contains links for more views (admin specific. hence the split). The problem I am having is that I can't seem to display views with the @RenderBody in the second partial view, which I understand is because you can't have more than one @RenderBody in a completed HTML file, which makes sense.

So my question is, how can I display the views in this manner? Also, probably more importantly, is this the correct way to achieve this 'sub menu' system I am trying for or is there a better way to achieve this?

Here is the relevant parts of the views, first is the 'main' _Layout file:

<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>Test App</h1>
            </div>
            <nav>
                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    @if (User.Identity.IsAuthenticated)
                    {
                        <li>@Html.ActionLink("Contracts", "List", "Contract", new { user=User.Identity.Name, page=1 }, null)</li>
                    }
                    @if (User.IsInRole("Administrator"))
                    {
                        <li id="admin">@Html.ActionLink("Administration", "Administration", "Home")</li>
                    }
                </ul>
            </nav>
        </header>
        <section id="main">
            @RenderBody()
        </section>
        <footer>
        </footer>
    </div>
</body>

When clicking the <li id="admin">@Html.ActionLink("Administration", "Administration", "Home")</li> link, then the Home controller return the second partial view shown below:

<header>
    <div id="admintitle">
        <h1>Administration</h1>
    </div>  
</header>
<body>
    <div id="div-1a">
         <nav>
            <ul id="adminmenu">
                <li>@Html.ActionLink("Contact", "List", "Contact")</li>
                <li>@Html.ActionLink("Home", "Index", "Home")</li>
            </ul>
        </nav>
    </div>
    <div id="div-1c">
        <h1>Test</h1>   
    </div>
    <section id="adminmain">
        @RenderBody()
    </section>
</body>

When I try and run the code, it fails due to the second @RenderBody, which is understandable.

If you need any more information, then please let me know.

Thanks very much.

2
  • What do you want to do in the RenderBody() of 'Administration' view? Commented Aug 10, 2012 at 8:40
  • @Mohayemin Show a View that will be returned by any of the ActionLinks in the Administration View. Commented Aug 10, 2012 at 8:58

4 Answers 4

6

You can not use @RenderBody() multiple times. One @RenderBody() in your main _Layout file is fair enough. In your second view use instead @RenderPartial() or @RenderAction.

UPDATE (based on first comment)

Let's say you want to render /Administrator/TheAction, so you will call

@{
    Html.RenderAction("TheAction", "Administrator");
}

TheAction action will look like this:

public PartialViewResult TheAction() {
    return PartialView();
}

And it will render the view in ~/Views/Administrator/TheAction.cshtml right inside the place from which you called the RenderAction().

The importance is that it does not accomplish another @RenderBody. As you can see in TheAction() example, you are returning PartialViewResult, which does not have any @RenderBody() helper.

Sign up to request clarification or add additional context in comments.

4 Comments

I looked into @RenderPartial, but it doesn't seem to be able to call a specific controller action, so I will try @RenderAction. However, how do you determine the location of the returned view? As in what @RenderBody accomplishes in the main partial view. Thanks very much.
Thanks for the extra information, however if I changed my @Html.ActionLink to @RenderAction() then the returned view would be placed within the <li> block, but I want it to appear in the @section block which currently holds the incorrect @RenderBody. This is why I think I might be going about this the wrong way!
I think you didn't understand my post correctly, try to read it few times more. Maybe you should consider reading something about partial views.
I understand the concept of partial views and how they work, it was just in this particular case I was a bit stuck, hence me questioning my use of them in this case. @Mohayemin below seems to have a possible solution. I guess I wasn't clear enough that I want the view to be shown when a user invokes it via an ActionLink.
3

Convert

From

<section id="adminmain">
   @RenderBody()
</section>

To

   <div id="adminmain"></div>

From

@Html.ActionLink("Contact", "List", "Contact")

To

@Ajax.ActionLink("Contact", "List", "Contact", new AjaxOptions { UpdateTargetId = "adminmain" })

And in ContactController List action return PartialView() instead of 'View()'.

Do not forget to include jquery.unobtrusive-ajax in your view to make Ajax work.

And before all read Roman's Answer

6 Comments

Sorry, but it doesn't work as expected. While the view is returned, it is returned as a basic, unformatted HTML page on its own (i.e. nothing of my partial views exist). Thanks anyway.
Did you notice the @Ajax instead of @Html for the ActionLink?
Yes I used the @Ajax, but it only returns the unformatted view with the surrounding Layout views.
Just realised I didn't have unobtrusive ajax referenced in my view! It works now I have added the following: <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
O yeah. Its my fault that I did not mention it in my answer. Sorry for that. I am modifying my answer so that others can have a quicker help.
|
0

I have found a solution which incorporates both @Roman Mazur and @Mohayemin's answers with an AJAX call.

I changed my View to the following as Roman suggested:

public PartialViewResult List() {
    return PartialView();
}

I then changed the section to a div as Mohayemin suggested:

<div id="adminmain"></div> 

I then created a link that invokes an AJAX script on it's click event:

<li><a id="load-partial">CONTACTS</a></li>

<script>
$(document).ready(function () {
    $('#load-partial').click(function () {
        $.ajax({
            url: '/Contact/List/',
            datatype: 'html',
            success: function (data) {
                $('#adminmain').empty().html(data);
            }
        });
    });
});
</script>

It works well, but I am always open to more suggestions on how to improve it.

Thanks very much.

Comments

0

I came across something interesting when working with nested partial views in MVC4. Just in case somebody stumbles upon this..

When rendering a partial view in a loop,

@Html.Partial()

does not work.

Instead, use @Html.RenderPartial(); that emits the HTML instead of HTML encoded in the former case and works.

Cheers!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.