Both login and registration form on one view of MVC3 Razor

My task is to create a registration and registration form in one submission!

I have a home look. On the home screen, I have two forms: login and registration. They are displayed using @ Html.RenderPartial (partial name, model). These forms apply to the account controller (login and registration actions).

Let me first give you the code ...

Models

// Login model, that contains email and password public class Login { … } // Registration model that contains minimum user data, eg name, surname public class Register { ... } // Model for home view which contains models for both register and login public class Home { public Login LoginModel { get; set; } public Register RegisterModel { get; set; } } 

Views : Index.cshtml

 @model App.Models.Home.Home <!-- Login Box --> @{ Html.RenderPartial("_LoginArea", Model.LoginModel); } <!-- Register Box --> @{ Html.RenderPartial("_RegisterArea", Model.RegisterModel); } 

_LoginArea.cshtml

 @model App.Models.Account.Login <div style="float: left; margin-right: 100px;"> <h1>@Localization.Account.Login</h1> <!-- Login form --> <div id="loginBox"> @using (Html.BeginForm("Login", "Account", FormMethod.Post)) { <div class="editor-label"> @Html.LabelFor(m => m.Email): </div> <div class="editor-field"> @Html.TextBoxFor(m => m.Email) @Html.ValidationMessageFor(m => m.Email) </div> <div class="editor-label"> @Html.LabelFor(m => m.Password): </div> <div class="editor-field"> @Html.PasswordFor(m => m.Password) @Html.ValidationMessageFor(m => m.Password) </div> <div class="editor-label"> @Html.CheckBoxFor(m => m.RememberMe) @Html.LabelFor(m => m.RememberMe) </div> <p> <input type="submit" value="@Localization.Account.Login" /> </p> } </div> </div> 

_RegisterArea.cshtml

 @model App.Models.Account.Register <div style="vertical-align: bottom;"> <h1>@Localization.Account.Register</h1> <div> @using (Html.BeginForm("Register", "Account")) { //same things like in login } ... 

Controllers

Homecontroller

 // // GET: /Home/ public ActionResult Index(Home model) { // // If logedin redirect to profile page // Else show home page view // if (Request.IsAuthenticated) { return RedirectToAction("Index", "User", new { id = HttpContext.User.Identity.Name }); } else { model.LoginModel = new Login(); model.RegisterModel = new Register(); return View(model); } } 

Just show the login action using the account controller

 // // POST: /Account/Login [HttpPost] public ActionResult LogIn(Login model) { if (Request.IsAuthenticated) { return RedirectToAction("Index", "User", new { id = HttpContext.User.Identity.Name }); } else { if (ModelState.IsValid) { if (model.ProcessLogin()) { return RedirectToAction("Index", "User", new { id = HttpContext.Session["id"] }); } } } //Note: this is problem part // If we got this far, something failed, redisplay form return View("~/Views/Home/Index.cshtml", model); } 

So everything works fine, BUT ! When the state of the model is invalid, I have the following exception

 The model item passed into the dictionary is of type 'App.Models.Account.Login', but this dictionary requires a model item of type App.Models.Home.Home'. 

I can bind these partial views and actions of the account to the Home model, but this is not what I need. I plan to use the _LoginArea.cshtml view in other views (for example, in the header of the "Custom view" page, and this view has a different model) So I need an action method (like Login) to retrieve the login model, not Home or whatever something else. But in this case, Im could not return the model to the Home view. How can I solve this problem? What is the best fit? Sorry for the big code, I just want clear things.

+4
source share
2 answers

SOLVE!!! Instead, Html.RenderAction is used. Here is the code:

Index.cshtml

 <!-- Login form --> @{ Html.RenderAction("Login", "Account"); } <!-- Register form --> @{ Html.RenderAction("Register", "Account"); } 

Partial views are the same ...

Controller actions:

 // // GET: /Account/Login public ActionResult Login() { Login model = new Login(); if (TempData.ContainsKey("Login")) { ModelStateDictionary externalModelState = (ModelStateDictionary)TempData["Login"]; foreach (KeyValuePair<string, ModelState> valuePair in externalModelState) { ModelState.Add(valuePair.Key, valuePair.Value); } } return View("_LoginHome", model); } // // POST: /Account/Login [HttpPost] public ActionResult Login(Login model) { if (Request.IsAuthenticated) { return RedirectToAction("Index", "User", new { id = HttpContext.User.Identity.Name }); } else { if (ModelState.IsValid) { if (model.ProcessLogin()) { return RedirectToAction("Index", "User", new { id = HttpContext.Session["id"] }); } } } TempData.Remove("Login"); TempData.Add("Login", ModelState); // If we got this far, something failed, redisplay form return RedirectToAction("Index", "Home"); } 

The same applies to Register actions.

As I can see, Im uses TempData strong> to store the state of the model, and then retrieves it when the action starts to render. Another question is how can we know where we should return if the state of the model is invalid. My solution is to parse Request.UrlReferrer as ataddeini said.

Many thanks to my friend Danilchuk Yaroslav for the help!

+2
source

In this case, I would recommend having a central login / registration page where users will be redirected when verification fails. For example, suppose this is a view called SiteAuthenticate

So, in the case of your LogIn(Login model) action, if the check fails, you always return a SiteAuthenticate that will be strongly typed to include the Login and Register models, very similar to your Home model.

This final way to make this approach work is to add the ReturnUrl property to your Login and Register models. This can be filled out as you want, or using something like Request.UrlReferrer . This way you will find out where to send the user back when they eventually log in / register.

So, when everything is said and done, your Login action might look something like this:

 [HttpPost] public ActionResult LogIn(Login model) { if (Request.IsAuthenticated) { return RedirectToAction(model.ReturnUrl); } else { if (ModelState.IsValid) { if (model.ProcessLogin()) { return RedirectToAction(model.ReturnUrl); } } } // If we got this far, something failed. Return the generic SiteAuthenticate view var saModel = new SiteAuthModel { LoginModel = model }; return View("~/Views/Account/SiteAuthenticate.cshtml", saModel); } 

Then your SiteAuthenticate will also return to the same Login action. Since you went through the original RetunrUrl, if the login is then successful, the use will be redirected to the original destination, as planned.

Hope this helps, let me know if something is unclear or does not make sense.

+3
source

All Articles