1

I have looked at multiple answers including the Partial View documentation https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial, https://www.codeproject.com/Articles/1108855/ways-to-Bind-Multiple-Models-on-a-View-in-MVC, Posting multiple forms on MVC Razor View with a Single View Model but I am unable to get their solutions to work.

I have a .NET Core 2.0 project with individual user authentication. I want my Login page to have the login form, the forgot password form, and the forgot password confirmation. I created Signin.chstml

@model LoginViewModel

<div class="m-login__signin">
    <div class="m-login__head">
        <h3 class="m-login__title">
            Sign In To Admin
        </h3>
    </div>
    <form class="m-login__form m-form" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
        <div asp-validation-summary="All" class="text-danger"></div>
        <div class="form-group m-form__group form-group">
            <input asp-for="Email" class="form-control m-input" type="text" placeholder="Email" name="email" autocomplete="off" />
            <span asp-validation-for="Email" class="text-danger"></span>
        </div>
        <div class="form-group form-group m-form__group">
            <input asp-for="Password" class="form-control m-input m-login__form-input--last" placeholder="Password" />
            <span asp-validation-for="Password" class="text-danger"></span>
        </div>
        <div class="row m-login__form-sub">
            <div class="col m--align-left m-login__form-left">
                <label asp-for="RememberMe" class="m-checkbox  m-checkbox--focus">
                    <input asp-for="RememberMe" />
                    @Html.DisplayNameFor(m => m.RememberMe)
                    <span></span>
                </label>
            </div>
            <div class="col m--align-right m-login__form-right">
                <a href="javascript:;" id="m_login_forget_password" class="m-link">
                    Forget Password ?
                </a>
            </div>
        </div>
        <div class="m-login__form-action">
            <button type="submit" class="btn btn-focus m-btn m-btn--pill m-btn--custom m-btn--air m-login__btn m-login__btn--primary">Log in</button>
        </div>
    </form>
</div>

and ForgotPass.cshtml

 @model ForgotPasswordViewModel

    <div class="m-login__forget-password">
        <div class="m-login__head">
            <h3 class="m-login__title">
                Forgotten Password ?
            </h3>
            <div class="m-login__desc">
                Enter your email to reset your password:
            </div>
        </div>
        <form class="m-login__form m-form" asp-action="ForgotPassword" method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group m-form__group">
                <input asp-for="Email" class="form-control m-input" type="text" placeholder="Email" name="email" id="m_email" autocomplete="off">
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="m-login__form-action">
                <button type="submit" class="btn btn-focus m-btn m-btn--pill m-btn--custom m-btn--air  m-login__btn m-login__btn--primaryr">
                    Request
                </button>
                &nbsp;&nbsp;
                <button id="m_login_forget_password_cancel" class="btn btn-outline m-btn m-btn--pill m-btn--custom m-login__btn">
                    Cancel
                </button>
            </div>
        </form>
    </div>

under the Shared folder.

I have a combined view model under Models

 public class LoginForgotPasswordComboViewModel
    {
        public LoginViewModel LoginViewModel { get; set; }
        public ForgotPasswordViewModel ForgotPasswordViewModel { get; set; }
    }

my Login.cshtml

@using System.Collections.Generic
@using System.Linq
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Http.Authentication
@model LoginForgotPasswordComboViewModel
@inject SignInManager<ApplicationUser> SignInManager


@Html.Partial("../Shared/SignIn.cshtml")
 @Html.Partial("ForgotPass")

and the Controller AccountController.cs

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;               
    if (ModelState.IsValid)
    {                   
        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
             return RedirectToLocal(returnUrl);
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");                    
            return View(model);
        }
    }

    return View(model);
}         

The view renders on the page. I am able to login with the correct username & pw. I can successfully request a password reset. My issues are this

1) If the username/password is not correct, I get the error

InvalidOperationException: The model item passed into the ViewDataDictionary is >of type 'TestProject.Models.AccountViewModels.LoginViewModel', but this >ViewDataDictionary instance requires a model item of type 'TestProject.Models.AccountViewModels.LoginForgotPasswordComboViewModel'.

I tried adding the following to the controller to return the LoginForgotPasswordComboViewModel

var return_model = new LoginForgotPasswordComboViewModel { LoginViewModel = model };
return View(return_model);

Which resulted in the error

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'TestProject.Models.AccountViewModels.LoginForgotPasswordComboViewModel', but this ViewDataDictionary instance requires a model item of type 'TestProject.Models.AccountViewModels.LoginViewModel'.

2) After I request a password reset it redirects me to the ForgotPassword page instead of the Login page - where is this controlled by default?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
user6383418
  • 395
  • 1
  • 4
  • 19
  • Because your not returning the correct view - your need to specify the view name or it will default to using the view with the same name as the POST method - `return View("Login", return_model);` –  Feb 06 '18 at 22:18
  • Thanks @StephenMuecke - I really thought that would work. I changed my return to return View("~/Views/Account/Login.cshtml", return_model); and I also tried return View("Login", return_model); and it still gave the error " InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'TestProject.Models.AccountViewModels.LoginForgotPasswordComboViewModel', but this ViewDataDictionary instance requires a model item of type 'TestProject.Models.AccountViewModels.LoginViewModel'." – user6383418 Feb 06 '18 at 22:29
  • Your `Login.cshtml` view has `@model LoginForgotPasswordComboViewModel` so that is the correct view to return. you could not be getting that error unless the code you have shown is not correct –  Feb 06 '18 at 22:56

1 Answers1

0

It works when I change

return View(model);

to

return View("~/Views/Account/Login.cshtml");

Without passing the model in

user6383418
  • 395
  • 1
  • 4
  • 19