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
Related
So, im currently building an application that needs the user model validating, and if the incorrect properties are filled in to the user it will tell them.
I have the data annotations set up, but im not sure how i relay the error message back to the user?
I have this set up so far on my model and view.
Model
public class DatabaseModel
{
[Required(ErrorMessage = ("A first name is required"))]
public string FirstName { get; set; }
[Required(ErrorMessage = ("A last name is required"))]
public string LastName { get; set; }
[Required(ErrorMessage = ("A valid role is required"))]
public string Role { get; set; }
// TODO - Validate rank to only b 1 - 10
//
[Range(1,10, ErrorMessage = ("A rank between 1 and 10 is required"))]
public int Rank { get; set; }
}
And View
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.Label("First Name")
<br>
#Html.TextBoxFor(m => m.FirstName)
<br>
#Html.Label("Last Name")
<br>
#Html.TextBoxFor(m=>m.LastName)
<br>
#Html.Label("Role")
<br>
#Html.TextBoxFor(m => m.Role)
<br>
#Html.Label("Rank")
<br>
#Html.TextBoxFor(m => m.Rank)
<br><br>
<input type="submit" value="Save">
}
My Controller
public class HomeController : Controller
{
// GET: Home
[HttpGet]
public ActionResult Index()
{
DatabaseModel model = new DatabaseModel();
return View(model);
}
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View();
}
}
I believe the model needs to be passed back to the view in order to display the error message, and although i have read through the documentation on asp.net i cannot understand how they just add the error message and the form knows how to display the errors to the user.
I am extremely confused.
You need to use ModelState.IsValid in your Controller and also #Html.ValidationMessageFor(model => model.FirstName) in your view:
public ActionResult Index(ViewModel _Model)
{
// Checking whether the Form posted is valid one.
if(ModelState.IsValid)
{
// your model is valid here.
// perform any actions you need to, like database actions,
// and/or redirecting to other controllers and actions.
}
else
{
// redirect to same action
return View(_Model);
}
}
For your example:
#model RoleCreatorAndEditor.Models.DatabaseModel
#{
ViewData["Title"] = "Index";
}
<h2>User Information</h2>
<p>This is your user information!</p>
#using (Html.BeginForm("Index", "Home", FormMethod.Post)) {
#Html.LabelFor(m=>m.FirstName)
<br>
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
<br>
#Html.LabelFor(m=>m.LastName)
<br>
#Html.TextBoxFor(m=>m.LastName)
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
. . .
<input type="submit" value="Save">
}
Controller:
[HttpPost]
public ActionResult Index(DatabaseModel model)
{
if (ModelState.IsValid)
{
ListToDatatable convert = new ListToDatatable();
DataTable user = convert.Convert(model);
DatabaseRepository dbRepo = new DatabaseRepository();
dbRepo.Upload(user);
}
return View(model);
}
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" />
}
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 must have something incorrectly setup as I can't get the UpdateModel function to correctly update my model based on information passed in via a FormCollection.
My View looks like:
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
<p>ID: #Html.TextBox("ID", Model.ID)</p>
<p>Forename: #Html.TextBox("Forename", Model.Forename)</p>
<p>Surname: #Html.TextBox("Surname", Model.Surname)</p>
<input type="submit" value="Submit" />
}
</fieldset>
My model is:
namespace NSLM.Models
{
public class Person
{
public int ID;
public string Forename;
public string Surname;
}
}
and my controller is:
[HttpPost]
public ActionResult Details(FormCollection collection)
{
try
{
// TODO: Add update logic here
Models.Person m = new Models.Person();
// This doesn't work i.e. the model is not updated with the form values
TryUpdateModel(m);
// This does work
int.TryParse(Request.Form["ID"], out m.ID);
m.Forename = Request.Form["Forename"];
m.Surname = Request.Form["Surname"];
return View(m);
}
catch
{
return View();
}
}
as you can see if I manually assign each property it works fine, so what have I not set that would get the model to be updated with the form values?
Thanks,
Mark
Replace fields with properties in your model, i.e.:
namespace NSLM.Models
{
public class Person
{
public int ID {get; set;}
public string Forename {get; set;}
public string Surname {get; set;}
}
}
By the time the call gets to the action method any automatic model binding has already been performed. Try changing the input parameter of your action method to accept a Person instance. In that case the model binder will try to create the instance and populate it from the values passed by your form.
try this :
view :
#model NSLM.Models.Person
#{
ViewBag.Title = "MVC Example";
}
<h2>My MVC Model</h2>
<fieldset>
<legend>Person</legend>
#using(#Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
<p>Forename: #Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</p>
<p>Surname: #Html.EditorFor(model => model.Surname)
#Html.ValidationMessageFor(model => model.Surname)
</p>
<input type="submit" value="Submit" />
}
</fieldset>
Controller :
[HttpPost]
public ActionResult Details(Person p)
{
if (ModelState.IsValid)
{
db.Entry(p).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(p);
}