5

I'm trying to get some MVC4 views to display in jQuery tabs. Here's my code:

<div id="tabs">
    <ul>
        <li><a href="#appone">App One</a></li>
        <li><a href="#apptwo">App Two</a></li>
    </ul>
    <div id="appone">
        @{ Html.RenderPartial("~/Views/AppOne/Index.cshtml"); }
    </div>
    <div id="apptwo">
        @{ Html.RenderPartial("~/Views/AppTwo/Index.cshtml"); }
    </div>
</div>

The problem is that the first tab content displays just fine- but the second is empty. It seems the partial view is not rendered.

Is there a way to either force jQuery tabs to update content for ALL tabs when the page is loaded, or a way to force my partial view to render on page load?

4
  • 1
    You should be invoking ActionResults for those, not feeding in the views directly. That's probably why it's not working for you, because it's not executing in the context of the framework. Commented Apr 16, 2013 at 22:23
  • Yeah I was doing that using jQuerys load('AppOne'). This worked, but I was having problems with the javascript. It also works with the tabs built in ajax loader, but the tab reloads with every click, losing form data. Caching the tabs didnt seem to work. Commented Apr 16, 2013 at 22:31
  • 1
    If they each have their own models, you simply need to use RenderAction rather than RenderPartial. That way you can feed them each a model of their own that they are expecting. By doing it the way you have they are both rendering under the context of the model you have at hand in that view. Commented Apr 16, 2013 at 22:42
  • That didnt work- replacing RenderPartial with RenderAction showed the first tab okay, but the second was again empty. Maybe I misunderstood? Commented Apr 16, 2013 at 22:58

1 Answer 1

4

Here's code I'm actively working on that is working just fine:

Login.cshtml

@{
    AjaxOptions optsLogin = new AjaxOptions { UpdateTargetId = "login-tab-login" };
    AjaxOptions optsRegister = new AjaxOptions { UpdateTargetId = "login-tab-register" };
}
<section id="login-tabs">
    <ul>
        <li><a href="#login-tab-login">Returning Customers</a></li>
        <li><a href="#login-tab-register">New Customers</a></li>
    </ul>
    @using (Ajax.BeginForm("Login", "Account", optsLogin, new { id = "form-login" }))
    {
        <div id="login-tab-login">
            @Html.Partial("Account/_Login")
        </div>
    }
    @using (Ajax.BeginForm("Register", "Account", optsRegister, new { id = "form-register" }))
    {
        <div id="login-tab-register">
            @Html.Partial("Account/_Register")
        </div>
    }
</section>
<script type="text/javascript">
    $(function() {
        $('#login-tabs').tabs();
    });
</script>

My partial views are in the Shared folder in an Account subfolder. In addition, each partial view has its own model. Other than that, the implementation is nothing special...

UPDATE

I've added code to implement Ajax calls on the two tabs. The code block above the tabs section holds the AjaxOptions objects for the two <form> elements. Your controller needs to look something like this:

AccountController.cs

public class AccountController : Controller
{
   ...
   [HttpGet]
   public ActionResult Login()
   {
       ...
       return View();
   }

   [HttpPost]
   public ActionResult Login(LoginModel model)
   {
       ...
       if (ModelState.IsValid)
           return RedirectToAction("Index", "Home")
       else
           return PartialView("/Account/_Login", model);
   }

   [HttpPost]
   public ActionResult Register(RegisterModel model)
   {
       ...
       if (ModelState.IsValid)
           return RedirectToAction("Index", "Home")
       else
           return PartialView("/Account/_Register", model);
   }
}

The Login GET action method renders the entire page, including the _Layout.cshtml and _ViewStart.cshtml views. My partial views, _Login.cshtml and _Register.cshtml, hold the HTML elements for the entry forms. Each partial view has its own submit button:

<input type="submit" value="<Whatever you want to display>" />

Because each partial view call is encased in its own using (Ajax.BeginForm(...)) block and I have given each <form> its own id attribute, I can add JavaScript code to hijack the submit event. Depending on which submit is pressed, it will execute the action method associated with the specified action and controller in the Ajax.BeginForm(...) call. In my case, if the form data passes validation, the controller will automatically redirect to /Home/Index.

However, if validation fails, the action method will simply send back the rendered version of my partial view to the browser and place it in the element specified in the UpdateTargetId property in the AjaxOptions object associated with the <form>. Since the default InsertionMode is Replace, the view engine will simply replace the old version of the partial view with the new version, complete with form data and validation messages, if included.

The only code-related items I haven't included are my partial views, which really don't matter as far as the jQuery tabs functionality goes. I don't have any additional JavaScript in my partial views, and the additional code in the AccountController I didn't include is specific to calling my external Web API console application and setting the authorization cookie. I am using Google's CDN for my jQuery and jQuery UI declarations, rather than hosting the JavaScript locally.

It takes a while to wrap your head around what you have to do. Once you've got it, though, you've got it and the knowledge is transferable. This is not WebForms, thank goodness.

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

7 Comments

You are using jQuery tabs? Doing anything fancy there? And your second tab shows just fine? There must be something else going on for me if this works for you. I'll check it out.
I added my jQuery call to the existing code, but this is all you need to get the tabs working. If you want to have each tab represent a form, which is what I'm doing, you have to wrap each DIV in an using block that calls Ajax.BeginForm(...). You just need to make sure you have an action method in the controller for each form action that gets called.
My tabs are working... just no content in that second tab- even though I know it works just fine outside the tabs, or in the first tab. My code on the razor side looks just like yours... although I'm not sure what you mean by 'wrapping each DIV in a using block that calls Ajax.BeginForm'... maybe that's what I'm missing.
I've changed my answer to explain in more detail exactly what I'm doing. Hopefully, it will work for you...
Thanks Neil! I'm going to mark this as the answer, even though the problem persists for me. I suspect there is more going on here for me- and you showed pretty well that it's possible to do what I want. Still no content in that second tab though- even if the Html.Partial link is identical. I suspect that some JS issues are breaking things for me, so I will try to isolate this further. Cheers on a great explanation.
|

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.