ASP.NET mvc RenderAction Login And Register Views

1.8k views Asked by At

I am working on NopCommerce v3.80. By Default the login and register views are different. I needed to merge them without changing much of the code, so I called @{ RenderAction("Register"); } inside Login.cshtml.

I also removed the layout (Layout = "~/Views/Shared/_ColumnsOne.cshtml";) from Register view.

the problem is when a validation error like 'Email Id already Exists!' comes, it goes to the register view. I need to show the validation or error message on the login view. but the login view accepts only Login Model.

Please see my code :

Register.cshtml

@model RegisterModel 
@using Nop.Web.Models.Customer;
@{
    //Layout = "~/Views/Shared/_ColumnsOne.cshtml";
}

<!-- Registeration fields  -->

Login.cshtml

@model LoginModel
@using Nop.Web.Models.Customer;
@{
    Layout = "~/Views/Shared/_ColumnsOneTT.cshtml";
}

@using (Html.BeginForm("Register", "Customer", FormMethod.Post)){
<div>  
@{ 
    Html.RenderAction("Register");
 }
<input type="submit" value="Submit"/>
}

CustomerController.cs - Register Method

public ActionResult Register(RegisterModel model){
    // Lot of code
    if (success){
    // lot of code
       return RedirectToRoute("RegisterResult");
    }
    foreach (var error in registrationResult.Errors)
       ModelState.AddModelError("", error);
    PrepareCustomerRegisterModel(model, true, customerAttributesXml);
    return View(model);

}

UPDATE : I checked how to work with two forms in a single view but it won't help me as I can not go with new model creation option.

UPDATE 2: I also tried with the new model creation option which covers the login and register models but I am still getting the same result.

4

There are 4 answers

0
Vishal_Kotecha On BEST ANSWER

Thank you all for your efforts.I had to create new model and wrap the two models Register and Login inside that.

it looks like how to work with two forms in a single view helped me a lot.

However, I am posting complete solution for a newbie.

CustomerController:

Loading the page

public ActionResult Login(bool? checkoutAsGuest)
        {
            var loginModel= new LoginModel();
            loginModel.UsernamesEnabled = _customerSettings.UsernamesEnabled;
            loginModel.CheckoutAsGuest = checkoutAsGuest.GetValueOrDefault();
            loginModel.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage;

            var registerModel = new RegisterModel();
            PrepareCustomerRegisterModel(registerModel, false);
            registerModel.Newsletter = _customerSettings.NewsletterTickedByDefault;

            return View(new LoginRegisterModel { LoginModel = , RegisterModel = registerModel });
        }

Login POST :

public ActionResult Login(LoginModel model, string returnUrl, bool captchaValid)
        {
        // Previous code as it is
        // Important! I Added this new line - to handle validation problems
        ModelState.Add("LoginValidation", null);
        //If we got this far, something failed, redisplay form
        model.UsernamesEnabled = _customerSettings.UsernamesEnabled;
        model.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage;

        var registerModel = new RegisterModel();
        PrepareCustomerRegisterModel(registerModel, false);
        //enable newsletter by default
        registerModel.Newsletter = _customerSettings.NewsletterTickedByDefault;

        return View(new LoginRegisterModel { LoginModel = model, RegisterModel = registerModel });
    } 

Register:

public ActionResult Register()
        {
            //check whether registration is allowed
            if (_customerSettings.UserRegistrationType == UserRegistrationType.Disabled)
                return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.Disabled });

            var model = new RegisterModel();
            PrepareCustomerRegisterModel(model, false);
            model.Newsletter = _customerSettings.NewsletterTickedByDefault;

            var loginModel = new LoginModel();
            loginModel.UsernamesEnabled = _customerSettings.UsernamesEnabled;
            loginModel.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage;

            return View("Login", new LoginRegisterModel { RegisterModel = model, LoginModel = loginModel });

        }

Register POST :

public ActionResult Register(RegisterModel model, string returnUrl, bool captchaValid, FormCollection form)
        {
            // previous code as it is
            // added this line to handle validations

            ModelState.Add("RegisterValidation", null);
            //If we got this far, something failed, redisplay form
            PrepareCustomerRegisterModel(model, true, customerAttributesXml);

            var loginModel = new LoginModel();
            loginModel.UsernamesEnabled = _customerSettings.UsernamesEnabled;
            //loginModel.CheckoutAsGuest = checkoutAsGuest.GetValueOrDefault();
            loginModel.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage;

            return View("Login", new LoginRegisterModel { RegisterModel = model, LoginModel = loginModel });
        }

Then created two partial Views - _LoginModel.cshtml and _registerModel.cshtml. In the views I added only one extra line

_LoginModel.cshtml:

if (!MvcHtmlString.IsNullOrEmpty(validationSummary) && ViewData.ModelState.ContainsKey("LoginValidation"))
    {
        <div class="message-error">@validationSummary</div>
    }

_RegisterModel.cshtml

@if (!MvcHtmlString.IsNullOrEmpty(validationSummary) && ViewData.ModelState.ContainsKey("RegisterValidation"))
    {
        <div class="message-error">@validationSummary</div>
    }

and finally, Login page

Login.cshtml

replaced register button (left panel with )

@using (Html.BeginForm("Register", "Customer", FormMethod.Post)){
<!-- divs and other elements -->
    @Html.Partial("_RegisterModel", Model.RegisterModel)
<!-- divs and other elements -->
}

and login form with

 @using (Html.BeginForm("Login", "Customer", new { returnUrl = Request.QueryString["returnUrl"] }, FormMethod.Post))
                       {
                        <!-- divs and other elements -->
                        @Html.Partial("_LoginModel", Model.LoginModel)
<!-- divs and other elements -->
                       }
2
Kevin Sijbers On

What you are trying to do is not really doable without more significant code changes then you have already done, since the validation is scoped to the specific model you are handling.

Probably the simplest effective solution I can think of, would be changing the Model of the Login view to be a wrapper around the other two models.

public class AuthenticationModel {
    public LoginModel Login {get;set;} 
    public RegisterModel Register {get;set;}
    public AuthenticationModel (LoginModel lModel, RegisterModel rModel) {
        Login = lModel;
        Register = rModel;
    }
}

This should solve most of the problem you are having, though the validation messages would have to be correctly targeted at your structure:

ModelState.AddModelError("", error);

in your code is replaced by:

ModelState.AddModelError("Register", error);
0
Travis Acton On

Apart from combining models into a view model to use that encompasses both models (like you probably should be doing in this case) a cheap and easy option would be to detect the error and pass it back as a viewbag property. Then on the front end you can check if that property is not null and display an error message if it is.

You should be creating a viewmodel though, everything else is more of a band-aid workaround.

0
Roger Perroni On

You could merge the login and register models into a brand new one,so validations could be adressed to the desired model.