Question

I am used to ASP.NET web forms, and am slowly learning ASP.NET MVC. My website has a little login form on the homepage. My natural thought is that this login form may be useful in other places, and it is not the primary action of the homepage, so I want to separate it off into a partial view. And because it is related to accounts, I want the login in my AccountController not my HomepageController.

Login form is a pretty basic strongly typed partial view:

@model Models.Account.AccountLogin

   <h2>Login Form</h2>

    @using (Html.BeginForm("_Login", "Account")) { 

        @Html.ValidationSummary()

        <div>
            <span>Email address:</span>
            @Html.TextBoxFor(x => x.EmailAddress)
        </div>

        <div>
            <span>Password:</span>
            @Html.PasswordFor(x => x.Password)
        </div>

        <div>
            <span>Remember me?</span>
            @Html.CheckBoxFor(x => x.RememberMe)
        </div>

        <input type="submit" value="Log In" />
    }

</div>

On the homepage, I have this:

@Html.Action("_Login", "Account")

Finally, in the account controller, this:

[HttpGet]
    public PartialViewResult _Login()
    {
        return PartialView();
    }

[HttpPost]
public PartialViewResult _Login(AccountLogin loginDetails)
{
    // Do something with this

    return PartialView();
}

Now when I load my homepage, it looks OK and contains the form. When I click the Log In button, it takes me to myurl/Account/_Login, which contains the form, but not within the _Layout master page, just basic plain HTML and it doesn't do anything at all when I click Log In.

login form after pressing Log In

I am pretty sure that I have just missed some fundamental aspect of what I am supposed to be doing here, can someone please point me in the right direction?

Was it helpful?

Solution

It's because you're returning a partial view, which strips away the master page and just returns the main content. Often actions starting with an underscore are used for partials (e.g. ajaxing in a bit of a page, but not the full page). It sounds like you want a full action, and not a partial, so

[HttpPost]
public ActionResult Login(AccountLogin loginDetails)
{
    // Do something with this

    return View();
}

OTHER TIPS

The issue here is that you are doing a fullpage postback.

You have two options, really.

Firstly, you can use a full page postback, and then call Html.Partial to display your Partial.

Something like

[HttpGet]
public ActionResult Login()
{
    return View();//this typically returns the view found at Account/Index.cshtml
}

And then create a View along the lines of

@{
    ViewBag.Title = "Index";
}

<h2>Title</h2>


@Html.Partial("PartialNameGoesHere")

Your partial is then rendered where indicated, but this is done when the page loads (if you look at the generated HTML, it appears exactly as though you had written it inline).

Or you can use jQuery/AJAX to load the partial on demand. Let's say you have a homepage of some description

public ActionResult Home()
{
    return View();
}

public ActionResult Login()
{
    return PartialView("_Login");
}

Create the view

@{
    ViewBag.Title = "Index";
}

<h2>Home</h2>
<div>
    <p>Hey welcome to my pretty awesome page!</p>
</div>
<a href="#" class="my-login-link">Show me the login!</a>
<div id="container">

</div>

You can then load the PartialView into the container div whenever you need it, using some JS.

$(function() {
    $('.my-login-link').click(function() {
        $.ajax({
            url: 'account/login',
            success: function(data) {
                $('#container').html(data);
            }
        });
        return false;//cancel default action
    });
});

In that instance, the page loads as normal without the login part. When the user clicks the link, the Login on the controller Account is called using AJAX/jQuery. This returns the HTML of the PartialView, which you can then add to the page using jQuery in the Success handler.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top