Let me explain a bit what I am trying to achieve . I have a View which has to Partial Views in it and each Partial Views have two different models .
1- LoginViewModel
2- RegisterViewModel
All i want to achieve is when the Login Post action occurs only Login Model to be returned to the Partial View with All validation messages if any field is left empty .
I am having a problem when I return the same view when there is any error in validating the fields .
Here is the piece of code
Account Controller:
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = User.SelectByUserNameAsync(model.UserName,model.Password);
if (user != null)
{
// var x = User.SignInAsync(model);
return Redirect("Home/Index");
}
else
{
ViewBag.Model = new RegisterViewModel();
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
LoginView :
#{
Layout = "~/Views/Shared/_LoginLayout.cshtml";
}
<section id="page-title">
<div class="container clearfix">
<h1>My Account</h1>
<ol class="breadcrumb">
<li>Home</li>
<li>Sign-Up</li>
<li class="active">Login</li>
</ol>
</div>
</section><!-- #page-title end -->
<!-- Content
============================================= -->
<section id="content">
<div class="content-wrap">
<div class="container clearfix">
<!--Login PartialView-->
#{Html.RenderPartial("_LoginBox");}
<!--Login PartialView Ends-->
<!--Register PartialView-->
#{Html.RenderPartial("_Register");}
<!--Register PartialView Ends-->
</div>
</div>
</section><!-- #content end -->
but when the page returns if any error is occured it shows me the error
The model item passed into the dictionary is of type 'ConnexMi.Models.LoginViewModel', but this dictionary requires a model item of type 'ConnexMi.Models.RegisterViewModel'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'ConnexMi.Models.LoginViewModel', but this dictionary requires a model item of type 'ConnexMi.Models.RegisterViewModel'.
Source Error:
Line 28:
Line 29: <!--Register PartialView-->
Line 30: #{Html.RenderPartial("_Register");}
Line 31:
Line 32: <!--Register PartialView Ends-->
Can you please tell me what I am doing wrong in this code ? Thanks
You will need a view model that combines both the Login and Register view models. For example
View models
public class LoginVM
{
[Display(Name = "Email")]
[Required(ErrorMessage = "Please enter an email address")]
[DataType(DataType.EmailAddress)]
[EmailAddress]
public string Email { get; set; }
[Required(ErrorMessage = "Please enter a password")]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
public class RegisterVM
{
// properties for email, password and confirm password
}
public class LoginRegisterVM
{
public LoginVM Login { get; set; }
public RegisterVM Register { get; set; }
}
Controller (assume Account)
public ActionResult Index()
{
LoginRegisterVM model = new LoginRegisterVM();
return View(model);
}
[HttpPost]
public ActionResult Login([Bind(Prefix="Login")]LoginVM loginModel)
{
if (!ModelState.IsValid)
{
LoginRegisterVM model = new LoginRegisterVM();
model.Login = loginModel;
return View("Index", model);
}
// Login and redirect
}
[HttpPost]
public ActionResult Register([Bind(Prefix="Register")]RegisterVM registerModel)
{
if (!ModelState.IsValid)
{
LoginRegisterVM model = new LoginRegisterVM();
model.Register = registerModel;
return View("Index", model);
}
// Register and redirect
}
View
#model LoginRegisterVM
#using(Html.BeginForm("Login", "Account", FormMethod.Post)
{
#Html.LabelFor(m => m.Login.Email)
#Html.TextBoxFor(m => m.Login.Email)
#Html.ValidationMessageFor(m => m.Login.Email)
... // other properties of login model
<input type="submit" value="Login" />
}
#using(Html.BeginForm("Register", "Account", FormMethod.Post)
{
// properties of register model
<input type="submit" value="Register" />
}
Related
I have two models, LoginModel and DatabaseModel. Combining them, I have created DatabaseCombinedWithOtherModel. The View, Login.cshtml is Strongly-Typed with the combined model. On running the Login.cshtml, the LoginModel returns null
I have all the necessary get and set methods
Here is the Controller class
namespace ReadingCat.Controllers
{
public class LoginController : Controller
{
private int userid;
// GET: Login
[HttpGet]
public ActionResult Login()
{
return View(new DatabaseCombinedWithOtherModel());
}
[HttpPost]
public ActionResult Login(DatabaseCombinedWithOtherModel model)
{
string realPassword = "";
string paswordFromUser = "";
string query = "SELECT password, userid FROM USERS WHERE username
= '" + model.loginModel.username + "'";
DataSet dataSet = model.databaseModel.selectFunction(query);
if (realPassword == paswordFromUser)
{
userid =
Convert.ToInt32(dataSet.Tables[0].Rows[0].ItemArray[1]);
model.loginModel.userid = userid;
return View("~/Views/Profile/Profile.cshtml",
model.loginModel);
}
else
return View();
}
}
}
Here is the Model:
namespace ReadingCat.Models
{
public class DatabaseCombinedWithOtherModel
{
public DatabaseModel databaseModel { get; set; }
public LoginModel loginModel { get; set; }
}
}
And here is the View:
#model ReadingCat.Models.DatabaseCombinedWithOtherModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
<!-- Main css -->
<link rel="stylesheet" href="~/css/login.css">
</head>
<body>
<div class="login-page">
<div class="form">
<form class="register-form">
</form>
#using (Html.BeginForm("Login", "Login", FormMethod.Post))
{
<form class="login-form">
#Html.TextBox("Username", null, new { placeholder = "Username",
#class = "login.css" })
#Html.ValidationMessageFor(model => model.loginModel.username);
#Html.Password("Password", null, new { placeholder = "Password",
#class = "login.css" })
#Html.ValidationMessageFor(model => model.loginModel.password);
<div class="form-submit">
<button type="submit" value="Submit" class="submit"
id="submit" name="submit">Login</button>
</div>
<p class="message">Not registered? Create an account</p>
</form>
}
</div>
</div>
<img class="coffee-image" src="~/images/coffee.gif" alt="">
It is giving the following error
System.NullReferenceException: 'Object reference not set to an instance
of
an object.'
ReadingCat.Models.DatabaseCombinedWithOtherModel.loginModel.get returned
null.
I think this error about not fully declare DatabaseCombinedWithOtherModelthis model.
Please try this code.
private int userid
DatabaseCombinedWithOtherModel modelobj = new DatabaseCombinedWithOtherModel();
// GET: Login
[HttpGet]
public ActionResult Login()
{
return View(modelobj);
}
And also try this thing that before call the model in view firstly add some
values in objects from controller side pass return View(modelobj);and then call in view side.
Since you are not seting the loginmodel, it will be null Which will throw the exception.
Either initialize the loginModel in the otherModel's constructor or in the Login get action.
Try
namespace ReadingCat.Models
{
public class DatabaseCombinedWithOtherModel
{
public DatabaseModel databaseModel { get; set; }
public LoginModel loginModel { get; set; }
}
public DatabaseCombinedWithOtherModel()
{
loginModel = new LoginModel();
databaseModel = new DatabaseModel();
}
}
or
[HttpGet]
public ActionResult Login()
{
var vm = new DatabaseCombinedWithOtherModel()
vm.loginModel = new LoginModel();
vm.databaseModel = new DatabaseModel();
return View(vm);
}
The mentioned error goes away if #Html.Textbox is replaced with #Html.EditorFor. Then, another error arises with the DatabaseModel. It returns null. So I created a different object of DatabaseModel and worked with that.
I'm developing a MVC 4 application with Entity Framework 6 and encountered a problem with Data Annotations. No matter what annotation I use, it isn't validate.
I'm using the following view model to render a form:
public class UserViewModel
{
[Required(ErrorMessage = "First Name is required")]
[Display(Name = "First Name")]
[StringLength(100, MinimumLength = 2)]
public string FirstName { get; set; }
...
}
In the view:
#Html.LabelFor(m => m.FirstName)
#Html.EditorFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
And it gets rendered:
<label for="FirstName">First Name</label>
<input class="text-box single-line" id="FirstName" name="FirstName" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="FirstName" data-valmsg-replace="true"></span>
When I click the submit button the client validation doesn't get triggered and in the controller the ModelState.IsValid is true.
UPDATE:
Controller:
[HttpGet]
public ActionResult Edit(int? Id)
{
var model = _userService.GetUserById(Id);
return View(model);
}
[HttpPost]
public ActionResult Edit(UserViewModel model)
{
if (ModelState.IsValid == true)
{
_userService.Save(model);
return View(model);
}
return View(model);
}
The problem is that you POST to your controller, you map to User object, not to UserViewModel. This is one of the reasons your server-side validation is not triggered.
As for the client-side validation, I'm not sure, but could be just the same error - your view is based on User type, rather than on UserViewModel.
So your controller should look like this:
[HttpGet]
public ActionResult Edit(int? Id)
{
var model = _userService.GetUserById(Id);
var viewModel = new UserViewModel()
{
FirstName = model.FirstName,
LastName = model.LastName,
// other properties
};
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(UserViewModel viewModel)
{
if (ModelState.IsValid == true)
{
var model = new User()
{
FirstName = viewModel.FirstName,
LastName = viewModel.LastName,
// other properties
};
_userService.Save(model);
return View(model);
}
return View(model);
}
And top of your Edit.cshtml you should have #model UserViewModel.
Hope this helps
After recreating my project I found out that the problem was the Ninject.Web.MVC dll. I didn't use this dll in my project so I removed it and it worked.
Title says it all, can anyone spot what I'm doing wrong. I've tried moving around my HTMlValidation Summary and a bunch of other things. I feel like it may have something to with the views I am returning from my Controller Class.
-- Model
public class Login
{
[Required(ErrorMessage = "First Name is required")]
[Display(Name = "First Namex")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Password is required")]
[DataType(DataType.Password)]
[Display(Name = "Passwordx")]
public string Password { get; set; }
}
-- Controller
[HttpPost]
public ActionResult Login(string FirstName, string Password)
{
if (ModelState.IsValid)
{
bool validLogin = new UserBAL().ValidateUser(FirstName, Password);
{
if (validLogin == true)
{
return RedirectToAction("Index", "Invoice");
}
else
{
return RedirectToAction("Index");
// ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
}
return View();
}
--View
#using (Html.BeginForm("Login", "Home"))
{ #Html.ValidationSummary(true)
<div>
<fieldset>
<legend>Login</legend>
<div class ="fields">
#Html.LabelFor(u => u.FirstName)
</div>
#Html.TextBoxFor(u => u.FirstName)
#Html.ValidationMessageFor(u => u.FirstName) <br />
<div class ="fields">
#Html.LabelFor(u => u.Password)
</div>
#Html.PasswordFor(u => u.Password) <br />
<input type="submit" value="Log In" />
</fieldset>
</div>
}
[HttpPost]
public ActionResult Login(EIAS.Models.Login login)
{
if (ModelState.IsValid)
{
bool validLogin = new UserBAL().ValidateUser(login);
{
if (validLogin == true)
{
return RedirectToAction("Index", "Invoice");
}
else
{
return RedirectToAction ("Index");
// ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
}
return View();
}
MVC in if (ModelState.IsValid) validate the model, and you don't receive the model in your action:
public ActionResult Login(string FirstName, string Password)
change the parameters of the action to:
public ActionResult Login(Login model)
and, for validate in the client, check if:
if you include jquery validate plugin (js)
check your web.config, keys ClientValidationEnabled and UnobtrusiveJavaScriptEnabled ir are in true.
check this link
You have to take the Model as the parameter to your Login action
public ActionResult Login()
{
// This will return the view with the form that you have above
return View();
}
[HttpPost]
public ActionResult Login(Login login)
{
// this is what your form will post too
if (ModelState.IsValid)
{
bool validLogin = new UserBAL().ValidateUser(login.FirstName, login.Password);
{
if (validLogin == true)
{
return RedirectToAction("Index", "Invoice");
}
else
{
return RedirectToAction("Index");
// ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
}
return View();
}
In the View just have
#using (Html.BeginForm())
Here is a link about MVC Razor Forms: http://blog.michaelckennedy.net/2012/01/20/building-asp-net-mvc-forms-with-razor/
Try that.
The problem was with my Views and which views I was returning. I was trying to use my Index view for validation, I hadn't created a Login View, so once I did that and added my validation to the Login View it worked. So in my question, Return View() wasn't really returning anything
I have the following View code:
#using (Html.BeginForm("Login", "Press", FormMethod.Post))
{
<fieldset>
<legend>User Registration</legend>
<div>
#Html.TextBoxFor(model => model.FullName)
#Html.ValidationMessageFor(model => model.FullName)
</div>
<div>
#Html.TextBoxFor(model => model.Company)
#Html.ValidationMessageFor(model => model.Company)
</div>
<div>
#Html.TextBoxFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div>
#Html.CheckBoxFor(model => model.JoinMailingList)
Please check this box to recieve a seasonal look book pdf and monthly newsletter
</div>
<p>
<input type="submit" value="Proceed" />
</p>
</fieldset>
}
And here is my Model:
public class UserViewModel
{
[Required(ErrorMessage = "Please enter your name.")]
[MaxLength(100)]
public string FullName { get; set; }
[Required(ErrorMessage = "Please enter the name of your company.")]
[MaxLength(50)]
public string Company { get; set; }
[Required(ErrorMessage = "Please enter your email.")]
[DataType(DataType.EmailAddress)]
[RegularExpression(#"^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+#((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$", ErrorMessage = "Please enter a valid email address.")]
[MaxLength(255)]
public string EmailAddress { get; set; }
public bool JoinMailingList { get; set; }
}
The problem is that when I click on the 'Proceed' button, none of the validation occurs. It just posts the action with no validation performed on it? Do I have to perform this inside the Controller?
Here is my Controller code:
public class PressController : Controller
{
//
// GET: /Press
public ViewResult Index()
{
return View();
}
//
// GET: /Press/Login
public ViewResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(UserViewModel userViewModel)
{
return RedirectToAction("Index", "Press");
}
}
Make sure that the action you are posting to takes the view model as argument:
[HttpPost]
public ActionResult Press(UserViewModel model)
{
// at this stage the validation has been performed during
// the process of model binding and now you could look in the
// modelstate if the model is vaild:
if (!ModelState.IsValid)
{
// validation failed => redisplay the view so that the user
// can fix his errors.
// Note that calling ModelState.IsValid doesn't trigger any validation
return View(model);
}
// at this stage we know that validation passed => we could do some processing
// and redirect
return RedirectToAction("Success");
}
or some people also use the TryUpdateModel method which also allows you to perform model binding which triggers the validation:
[HttpPost]
public ActionResult Press()
{
var model = new UserViewModel();
// validation will be triggered at this stage and the method
// will return true or false based on the result of this validation
if (!TryUpdateModel(model))
{
// validation failed => redisplay the view so that the user
// can fix his errors.
return View(model);
}
// at this stage we know that validation passed => we could do some processing
// and redirect
return RedirectToAction("Success");
}
And if you want to enable client side validation, just make sure that the following 2 scripts are referenced in your page:
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
Do you have the client-validation enabled?
See "Step 3: Enabling Client-side Validation" in ScottGu's post
In the server side you must check is the model valid using
ModelState.IsValid
I don't know if I am explaining this correctly, or if the solution is rather simple, so here goes:
I am using MvcMailer, but before that I set up a wizard input form which I call Quote.cshtml. Behind Quote.cshtml, I set up a model called QuoteModel.cs.
Quote.cshtml at its most basic (I am leaving out all of the wizard logic and only showing one input):
<td width="40%">
#Html.LabelFor(m => m.FirstName, new { #class = "mylabelstyle", title = "Enter first name." })
</td>
<td width="60%">
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</td>
QuoteModel.cs (again, only showing the one input; n.b.: using the DataAnnotationExtensions)
public class QuoteModel
{
[Required(ErrorMessage = "First Name required.")]
[Display(Name = "First Name:")]
public string FirstName { get; set; }
}
Now I am trying to integrate MvcMailer, which sets up IQuoteMailer.cs, QuoteMailer.cs, _Layout.cshtml, and QuoteMail.cshtml. The QuoteMail.cshtml is what the recipient of the mail will eventually see. I also set up a QuoteController.cs, in which I placed the appropriate code required by MvcMailer. It is in the QuoteMailer.cs and QuoteController.cs where I am having trouble passing the user input from Quote.cshtml (which is based on the model in QuoteModel.cs).
IQuoteMailer.cs:
public interface IQuoteMailer
{
MailMessage QuoteMail();
}
QuoteMailer.cs:
public class QuoteMailer : MailerBase, IQuoteMailer
{
public QuoteMailer():
base()
{
MasterName="_Layout";
}
public virtual MailMessage QuoteMail()
{
var mailMessage = new MailMessage{Subject = "QuoteMail"};
mailMessage.To.Add("some-email#example.com");
ViewBag.Data = someObject;
//I imagine this is where I can pass my model,
//but I am not sure (do I have to iterate each and
//every input (there are like 20 in QuoteModel.cs)?
return mailMessage;
}
QuoteMail.cshtml (_Layout.cshtml is pretty standard, so not showing here):
#*HTML View for QuoteMailer#QuoteMail*#
Welcome to MvcMailer and enjoy your time!<br />
<div class="mailer_entry">
<div class="mailer_entry_box">
<div class="mailer_entry_text">
<h2>
INSERT_TITLE
</h2>
<p>
INSERT_CONTENT
//I believe I am going to use a "#" command like #ViewData
//to pass FirstName, but again, not sure how to bind
//the model and then pass it.
</p>
<p>
INSERT_CONTENT
</p>
</div>
</div>
</div>
And finally, the relevant parts of the QuoteController.cs (note that I have am using a wizard, therefore, part of my problem is figuring out where to put the MvcMailer code, but I think I may have it right):
public class QuoteController: Controller
{
/// <summary>
/// MvcMailer
/// </summary>
private IQuoteMailer _quoteMailer = new QuoteMailer();
public IQuoteMailer QuoteMailer
{
get { return _quoteMailer; }
set { _quoteMailer = value; }
}
//
// GET: /Quote/
[HttpGet]
public ActionResult Quote()
{
HtmlHelper.ClientValidationEnabled = true;
HtmlHelper.UnobtrusiveJavaScriptEnabled = true;
//In order to get the clientside validation (unobtrusive),
//the above lines are necessary (where action takes place)
return View();
}
//
// POST: /Matrimonial/
[HttpPost]
public ActionResult Quote(QuoteModel FinishedQuote)
{
if (ModelState.IsValid)
{
QuoteMailer.QuoteMail().Send();
return View("QuoteMailSuccess", FinishedQuote);
}
else return View();
}
//
// POST: /Matrimonial/Confirm
[HttpPost]
public ActionResult QuoteMailConfirm(QuoteModel FinishedQuote)
{
return PartialView(FinishedQuote);
}
}
So, my confusion is to how to pass the QuoteModel I created, so that ultimately I can take the user inputed data and then generate the MvcMailer view.
I appreciate the communities help.
You could have the IQuoteMailer interface take the model:
public interface IQuoteMailer
{
MailMessage QuoteMail(QuoteModel model);
}
and in the implementation use this model:
public class QuoteMailer : MailerBase, IQuoteMailer
{
public QuoteMailer() : base()
{
MasterName = "_Layout";
}
public virtual MailMessage QuoteMail(QuoteModel model)
{
var mailMessage = new MailMessage
{
Subject = "QuoteMail"
};
mailMessage.To.Add("some-email#example.com");
// Use a strongly typed model
ViewData = new ViewDataDictionary(model);
PopulateBody(mailMessage, "QuoteMail", null);
return mailMessage;
}
}
then from the controller when you decide to send the mail pass the model:
[HttpPost]
public ActionResult Quote(QuoteModel FinishedQuote)
{
if (ModelState.IsValid)
{
QuoteMailer.QuoteMail(FinishedQuote).Send();
return View("QuoteMailSuccess", FinishedQuote);
}
else return View();
}
and finally in the template (~/Views/QuoteMailer/QuoteMail.cshtml) you could use the model:
#using AppName.Models
#model QuoteModel
Welcome to MvcMailer and enjoy your time!
<br />
<div class="mailer_entry">
<div class="mailer_entry_box">
<div class="mailer_entry_text">
<h2>
INSERT_TITLE
</h2>
<p>
Hello #Model.FirstName
</p>
<p>
INSERT_CONTENT
</p>
</div>
</div>
</div>