When I click the login button I never get my model posted to the server. However if I accept a FormCollection I will see the values. How can I make this automatically bind to my model instead of searching the Form Collection?
From what I have read there are a few common problems for this:
1 - your view does not specify what model you are using (#model myApp.Models.name)
2 - Your model does not use properties
3 - Any of the required fields are missing
Controller
[HttpGet]
public ActionResult Password()
{
return View(new AuthViewModel());
}
[HttpPost]
public ActionResult Password(AuthViewModel password)
{
if (password == null || string.IsNullOrEmpty(password.Password))
{
ViewBag.Error = Constants.ErrorMessages.UserPassword_PassBlank;
return View(new AuthViewModel());
}
//success
return Redirect("/");
}
Model
public class AuthViewModel
{
public string Password { get; set; }
}
View
#model MvcApplication1.Models.AuthViewModel
#{
ViewBag.Title = "Password";
}
<h2>Password</h2>
#using (Html.BeginForm())
{
<div>#Html.TextBoxFor(m => m.Password,new{placeholder="Password",type="password",autofocus=""})</div>
<div><button id="btnLogin" type="submit">Login</button></div>
<div class="error">#ViewBag.Error</div>
}
Not sure why Dan's answer isn't working without trying it, looks like it should.
I took a look at some of my code for a login form, similar to yours.
Here's mine :
public class SignInModel
{
[Required]
[Display(Name = "Enter your email address")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Enter your password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
The main difference I see is that mine has the [DataType(DataType.Password)] attribute on the password. Not sure if this makes that much difference though.
The other thing I noticed is different is that in my form I specify that the form method is POST. Also I've used the EditorFor() helper instead of textbox or password:
#using (Html.BeginForm("SignIn", "Account", "POST"))
{
<div class="form-field">
#Html.LabelFor(x => x.Email)
#Html.EditorFor(m => m.Email)
</div>
<div class="form-field">
#Html.LabelFor(x => x.Password)
#Html.EditorFor(m => m.Password)
</div>
<div class="form-remember">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(x => x.RememberMe)
</div>
<button type="submit">
Sign In</button>
}
use the following
#using (Html.BeginForm())
{
<div>#Html.PasswordFor(model => model.Password)</div>
<div><input id="btnLogin" type="submit" value="Login"/></div>
<div class="error">#ViewBag.Error</div>
}
Related
I am getting an issue. While using Login and Registration views in a single page. When main view page loaded. I got an error pop up:
You are using dublicating email & password.
Dublicating email & password may lead to big mess.
Please note that it says dublicating not duplicating.
Have any one help me. My code is below.
My Modal is:
public class LoginViewModel {
public string Email { get; set; }
public string Password { get; set; }
}
public class RegisterViewModel {
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
}
public class RegisterLoginViewModel
{
public LoginViewModel LoginViewModel { get; set; }
public RegisterViewModel RegisterViewModel { get; set; }
}
My Controller:
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
var model = new RegisterLoginViewModel();
model.LoginViewModel = new LoginViewModel();
model.RegisterViewModel = new RegisterViewModel();
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
Main View:
#model OpenOrderFramework.Models.RegisterLoginViewModel
#Html.Partial("_RegisterForm", Model.RegisterViewModel
#Html.Partial("_LoginForm", Model.LoginViewModel)
Partial View _LoginForm:
#model OpenOrderFramework.Models.LoginViewModel
#using (Html.BeginForm("Login", "Account", FormMethod.Post))
{ #Html.TextBoxFor(x => x.Email)
#Html.PasswordFor(x => x.Password)
<input type="submit" value="Log In" />
}
Partial View _RegisterForm:
#model OpenOrderFramework.Models.RegisterViewModel
#using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
#Html.TextBoxFor(x => x.Email)
#Html.PasswordFor(x => x.Password)
<input type="submit" value="Register" />
}
Have you tried to rename your properties Email and Password from either or both of the RegisterViewModel and LoginViewModel? It's possible that is causing the problem.
I'm very new with MVC, so bear with me, but I can't seem to bind a value from a SelectList to an instance of the selected object during a postback in MVC 4.
Suppose I have to create a Teacher as a member of a School. I have a ViewModel class defined as such:
public class RegisterTeacherModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
[Required]
[Display(Name = "School")]
public School SelectedSchool { get; set; }
[ScaffoldColumn(false)]
public Guid UserId
{
get;
set;
}
public SelectList PossibleSchools
{
get;
private set;
}
public RegisterTeacherModel(IRepository<School> schoolRepo)
{
PossibleSchools = new SelectList(schoolRepo, "Id", "Name");
}
}
And my View:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>RegisterTeacherModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SelectedSchool)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedSchool, Model.PossibleSchools)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
And finally, my Controller method:
[HttpPost, ActionName("Create")]
public ActionResult CreateTeacher(RegisterTeacherModel teacherModel)
{
if (ModelState.IsValid)
{
try
{
...
}
}
}
But when I receive the RegisterTeacherModel object back in my Create method in the Controller, SelectedSchool is always null. I must be missing something in the way the model binder re-creates the object references on postback. Can anyone point me in the right direction?
I think you touched on it with your second post: Try pointing the initial code to Model.SelectedSchool.<IdProperty>
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedSchool.**<IdProperty>**, Model.PossibleSchools)
</div>
Well, I found a workaround. I still don't know if I'm missing something, but instead of using a School object in my ViewModel, I replaced it with the SelectedSchoolId as such:
public class RegisterTeacherModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
[Required]
[Display(Name = "School")]
public int SelectedSchoolId { get; set; }
...
}
And change my View dropdown to use this instead:
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedSchoolId, Model.PossibleSchools)
</div>
And then in my controller, when creating the real model objects I can simply pull the School object from the School repository and associate it with the real Teacher object.
I have a page with the same input box added a number of times.
#Html.TextBoxFor(m => m.Product)
#Html.TextBoxFor(m => m.Product)
#Html.TextBoxFor(m => m.Product)
How to I bind this to the Model.
I've tried:
public class Shop
{
public string ShopName { get; set; }
[Remote("ProductExists", "Account", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public List<String> Product { get; set; }
}
But I can only ever see the data in the first field. Also I tried:
#Html.TextBoxFor(m => m.Product[0])
#Html.TextBoxFor(m => m.Product[1])
#Html.TextBoxFor(m => m.Product[2])
But remote validation doesn't work so I'm a little stumped here. Essential what I would like to achieve is to send the list of products with the shop so that it can be validated via a remote call to a function. I tried putting the products within there own public class but then I wasn't able to access the shop name from within that class.
This is the Controller Action I'm trying to use:
public JsonResult ProductExists(List<String> Product, string ShopName)
Any Ideas how I could solve this would be so much appreciated?
EDIT
This Semi works but remote validation still isn't passing ShopName:
public class Shops
{
[Required]
public string ShopName { get; set; }
public List<Products> Product { get; set; }
}
public class Products
{
[Required]
[Remote("ProductExists", "Home", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public String Product { get; set; }
}
Controller Action:
public JsonResult ProductExists(List<String> Product, string ShopName)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
View:
#model Shop.Models.Shops
#{
ViewBag.Title = "Shop";
}
<h2>Shop</h2>
<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>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Shop</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ShopName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ShopName)
#Html.ValidationMessageFor(model => model.ShopName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[0])
#Html.ValidationMessageFor(model => model.Product[0])
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[1])
#Html.ValidationMessageFor(model => model.Product[1])
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[2])
#Html.ValidationMessageFor(model => model.Product[2])
</div>
<input type="submit" value="Create" />
</fieldset>
}
look at the following answer. I would make product a class on its own like you tried. Loot at the rendered html code for the page and check out the field name for the ShopName textbox. I think it should be ShopName, if so you dont need to change the AdditionalFields attribute if not change it to the name rendered.
So something like this:
public class Shop
{
public string ShopName { get; set; }
public List<Products> Product { get; set; }
}
public class Products
{
[Remote("ProductExists", "Account", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public String Product { get; set; }
}
in your view do something like this:
foreach(var item in Model.products)
{
#Html.TextBoxFor(item => item.product) // not sure if the syntax is right
}
I am trying to get ASP.NET MVC 3 to generate forms from complex, nested objects. There is one validation behaviour I found which was unexpected and I am not sure if it's a bug in the DefaultModelBinder or not.
If I have two objects, lets call the "parent" one "OuterObject", and it has a property of type "InnerObject" (the child):
public class OuterObject : IValidatableObject
{
[Required]
public string OuterObjectName { get; set; }
public InnerObject FirstInnerObject { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(OuterObjectName) && string.Equals(OuterObjectName, "test", StringComparison.CurrentCultureIgnoreCase))
{
yield return new ValidationResult("OuterObjectName must not be 'test'", new[] { "OuterObjectName" });
}
}
}
Here is InnerObject:
public class InnerObject : IValidatableObject
{
[Required]
public string InnerObjectName { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(InnerObjectName) && string.Equals(InnerObjectName, "test", StringComparison.CurrentCultureIgnoreCase))
{
yield return new ValidationResult("InnerObjectName must not be 'test'", new[] { "InnerObjectName" });
}
}
}
You will notice the validation I put on both.. just some dummy validation to say some value can't equal "test".
Here is the view that this will display in (Index.cshtml):
#model MvcNestedObjectTest.Models.OuterObject
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm()) {
<div>
<fieldset>
<legend>Using "For" Lambda</legend>
<div class="editor-label">
#Html.LabelFor(m => m.OuterObjectName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.OuterObjectName)
#Html.ValidationMessageFor(m => m.OuterObjectName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.FirstInnerObject.InnerObjectName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.FirstInnerObject.InnerObjectName)
#Html.ValidationMessageFor(m => m.FirstInnerObject.InnerObjectName)
</div>
<p>
<input type="submit" value="Test Submit" />
</p>
</fieldset>
</div>
}
..and finally here is the HomeController:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new OuterObject();
model.FirstInnerObject = new InnerObject();
return View(model);
}
[HttpPost]
public ActionResult Index(OuterObject model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(model);
}
}
What you will find is that when the model gets validated by the DefaultModelBinder, the "Validate" method in "InnerObject" gets hit twice, but the "Validate" method in "OuterObject" does not get hit at all.
If you take off IValidatableObject from "InnerObject", then the one on "OuterObject" will get hit.
Is this a bug, or should I expect it to work that way? If I should expect it to, what's the best workaround?
This answer is just to provide one workaround I have just thought of - so it is not really an answer! I am still not sure if this is a bug or what the best workaround is, but here is one option.
If you remove the custom validation logic from "InnerObject" and incorporate it into "OuterObject" it seems to work fine. So basically this works around the bug by only allowing the top-most object to have any custom validation.
Here is the new InnerObject:
//NOTE: have taken IValidatableObject off as this causes the issue - we must remember to validate it manually in the "Parent"!
public class InnerObject //: IValidatableObject
{
[Required]
public string InnerObjectName { get; set; }
}
And here is the new OuterObject (with the Validation code stolen from InnerObject):
public class OuterObject : IValidatableObject
{
[Required]
public string OuterObjectName { get; set; }
public InnerObject FirstInnerObject { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(OuterObjectName) && string.Equals(OuterObjectName, "test", StringComparison.CurrentCultureIgnoreCase))
{
yield return new ValidationResult("OuterObjectName must not be 'test'", new[] { "OuterObjectName" });
}
if (FirstInnerObject != null)
{
if (!string.IsNullOrWhiteSpace(FirstInnerObject.InnerObjectName) &&
string.Equals(FirstInnerObject.InnerObjectName, "test", StringComparison.CurrentCultureIgnoreCase))
{
yield return new ValidationResult("InnerObjectName must not be 'test'", new[] { "FirstInnerObject.InnerObjectName" });
}
}
}
}
This works as I would expect, hooking up the validation error to each field correctly.
It is not a great solution because if I need to nest "InnerObject" in some other class, it does not share that validation - I need to replicate it. Obviously I could have a method on the class to store the logic, but each "parent" class needs to remember to "Validate" the child class.
I am not sure this is a problem with MVC 4 anymore, but...
If you use partial views made just for your InnerObjects, they will validate correctly.
<fieldset>
<legend>Using "For" Lambda</legend>
<div class="editor-label">
#Html.LabelFor(m => m.OuterObjectName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.OuterObjectName)
#Html.ValidationMessageFor(m => m.OuterObjectName)
</div>
#Html.Partial("_InnerObject", Model.InnerObject)
<p>
<input type="submit" value="Test Submit" />
</p>
</fieldset>
Then add this partial "_InnerObject.cshtml":
#model InnerObject
<div class="editor-label">
#Html.LabelFor(m => m.InnerObjectName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.InnerObjectName)
#Html.ValidationMessageFor(m => m.InnerObjectName)
</div>
Should you have made OuterObject base class for InnerObject instead of creating a relationship as you did? (Or vice versa) and provide the view the base object as the ViewModel?
This will mean that when model binding the default constructor of the OuterObject (or which ever class is your base) will be called indirectly invoking Validate on both objects.
i.e.
Class:
public class OuterObject : InnerObject, IValidateableObject
{
...
}
View:
#model MvcNestedObjectTest.Models.OuterObject
Controller Action:
public ActionResult Index(OuterObject model)
I am still struggling with learning ASP.NET MVC. All my form entries are required so I would like to do validation on them. For brevity I have paired my model down to Description (textbox) and Paradigm (dropdown). I am including Entry.cs, Paradigm.cs and EntryViewModel.cs Model classes and the Display.cshtml View.
[Bind(Exclude = "EntryId")]
public class Entry
{
[ScaffoldColumn(false)]
public int EntryId { get; set; }
[Required(ErrorMessage = "You must include a description.")]
public string Description { get; set; }
[Display(Name = "Type")]
[Required(ErrorMessage = "You must select a type.")]
public int ParadigmId { get; set; }
public virtual Paradigm Paradigm { get; set; }
}
public class Paradigm
{
[ScaffoldColumn(false)]
public int ParadigmId { get; set; }
[Required]
public string Name { get; set; }
public List<Entry> Entries { get; set; }
}
public class EntryViewModel
{
public Entry Entry { get; set; }
public IEnumerable<Entry> Entries { get; set; }
}
#model Pylon.Models.EntryViewModel
#{
ViewBag.Title = "Display";
}
<hr />
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Entry</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Entry.Description)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Entry.Description)
#Html.ValidationMessageFor(model => model.Entry.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Entry.ParadigmId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Entry.ParadigmId, ((IEnumerable<Pylon.Models.Paradigm>)ViewBag.PossibleParadigms).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Name),
Value = option.ParadigmId.ToString(),
Selected = (Model != null) && (option.ParadigmId == Model.Entry.ParadigmId)
}))
<img src="../../Content/Images/add_icon.gif" />
#Html.ValidationMessageFor(model => model.Entry.ParadigmId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
If I submit the form without entering a description I would like validation to kick in and say "You must include a description." However instead I receive an ArgumentNullException on the DropDownFor line. http://www.wvha.org/temp/ArgumentNullException.png
What should I be doing? As an aside any decent books that cover ASP.NET MVC 3/Razor. I can follow along the basic tuts, but I go astray when I need to deviate to more advance features.
public class EntriesController : Controller
{
private readonly PylonContext _context = new PylonContext();
public ActionResult Display()
{
// DropDown
ViewBag.PossibleParadigms = _context.Paradigms;
var viewModel = new EntryViewModel {Entries = _context.Entries.ToList()};
return View(viewModel);
}
[HttpPost]
public ActionResult Display(EntryViewModel viewModel)
{
if (ModelState.IsValid)
{
_context.Entries.Add(viewModel.Entry);
_context.SaveChanges();
return RedirectToAction("Display");
}
return View(viewModel);
}
}
It's quite difficult to say without seeing your controller code, but looks like your ViewBag.PossibleParadigms might be null.
Does your insert/update controller action look something like this?
if (ModelState.IsValid) {
///...
} else {
return View(model);
}
If so, you need to put the PossibleParadigms back into the ViewBag (so to speak) before you return back to the view.
If you can post the relevant controller action code, it would be easier to know for sure.