ASP.Net MVC3 Remote Data Annotation Not Working - data-annotations

I have a class with data annotations using the Remote attribute:
public class Person
{
[Remote("NameValidation","Validation", ErrorMessage = "Field is Invalid", Fields = "LastName")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
In ValidationController:
public ActionResult NameValidation(string FirstName, string LastName)
{
bool isNameValid = true;
if (FirstName.Contains("John") && LastName.Contains("Doe"))
{
isNameValid = false;
}
return Json(isNameValid, JsonRequestBehavior.AllowGet);
}
In the view I have:
#{Html.EnableClientValidation(); }
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.FirstName) #Html.ValidationMessageFor(x => x.FirstName)
#Html.EditorFor(x => x.LastName) #Html.ValidationMessageFor(x => x.LastName)
<input name="finishButton" type="submit" id="button" >
}
The NameValidation only gets called if I also add the Required attribute like this:
public class Person
{
[Required]
[Remote("NameValidation","Validation", ErrorMessage = "Field is Invalid", Fields = "LastName")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
How do I get the Remote validation to work without having to have the Required validation?

Update to MVC3 RC2. I just tried it without the Required attribute. Its working.
One thing I found strange though, is that the remote validation is sometimes fired on every key press and onchange the other times

Related

Model binding not working properly for Sitecore.Data.ID type while submitting through a Form

I am experiencing a model binding problem, My id is of type Sitecore.Data.ID. After submitting the form, all the other fields gets bonded with correct data, however my Id gets changed to something else.
For example, in the form, the value for hidden field 'id' is 2fb3169c-8b3f-4618-ac78-6170fd0eb297, after submitting to CartController, the value becomes {{68CE2980-7611-422B-96E1-29C4CC0132D5}} or {{82F7914C-34D6-4009-B301-53C1499774A3}} or something else.
I think its random. I am not sure where I going wrong.
I have a Model like this:
[SitecoreType(AutoMap = true,Cachable = true)]
public class Book : Item
{
public virtual ID Id { get; set; }
[SitecoreField(IsRequired = true)]
public virtual string Name { get; set; }
[SitecoreField(IsRequired = true)]
public virtual double Price { get; set; }
[SitecoreField(IsRequired = true)]
[StringLength(50, MinimumLength = 5)]
public virtual string Description { get; set; }
}
This is my view:
#model Book
using (Html.BeginRouteForm(Sitecore.Mvc.Configuration.MvcSettings.SitecoreRouteName, FormMethod.Post)
)
{
#Html.Sitecore().FormHandler("Cart", "Index")
#Html.HiddenFor(x => Model.Id)
<div>
#Html.DisplayFor(x => book.Name)
#Html.EditorFor(x => book.Name, new { #class = "bold" })
</div>
<div>
#Html.DisplayFor(x => book.Price)
#Html.EditorFor(x => book.Price, new { #class = "bold" })
</div>
<div>
#Html.DisplayFor(x => book.Description)
#Html.EditorFor(x => book.Description, new { #class = "bold" })
</div>
<input type="submit" />
}
This is the cart controller:
public class CartController : GlassController
{
[HttpPost]
public ActionResult Index(Book book)
{
string id = book.Id.ToString();
if (!string.IsNullOrEmpty(id))
{
book = SitecoreContext.GetItem<Book>(new Guid(id), false, true);
return PartialView("~/Views/Cart/details.cshtml", book);
}
return Redirect("http://google.com");
}
}
The Id represents the item Id in the glass mapper. So, instead of using
public virtual ID Id { get; set; }
change it to this one:
[SitecoreId]
public virtual Guid Id { get; set; }

ViewData item key 'SelectedAustraliaStateId' is of type 'System.String' but must be of type 'IEnumerable<SelectListItem>'

I am not able to save the record after implementing the drop down field. Can someone please correct my code.
Create.cshtml
#model SomeIndianShit.Models.Accommodation
#{
ViewBag.Title = "Advertise Accommodation";
}
<form name="datapluspics" method="post" enctype="multipart/form-data">
#Html.ValidationSummary(true)
<fieldset>
<legend>Accommodation</legend>
<div class="editor-label">
#Html.LabelFor(model => model.State)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SelectedAustraliaStateId, Model.AustraliaStates)
</div>
<p>
<input type="submit" value=" Save " />
</p>
</fieldset>
</form>
My Model:
public class AustraliaStates
{
[Key]
public string AustraliaStateId { get; set; }
public string AustraliaStateName { get; set; }
}
public class Accommodation
{
[Key]
public string A_Unique_Id { get; set; }
[Display(Name = "Ad Id")]
public string Ad_Id { get; set; }
[Display(Name = "Posted By")]
public string User { get; set; }
[Display(Name = "Street")]
public string Street { get; set; }
[Required]
[Display(Name = "Suburb")]
public string Suburb { get; set; }
[Required]
[Display(Name = "State")]
public string State { get; set; }
public byte[] Picture1 { get; set; }
public string SelectedAustraliaStateId { get; set; }
public IEnumerable<SelectListItem> AustraliaStates { get; set; }
}
AccommodationController.cs
// GET: /Accommodation/Create
[Authorize]
public ActionResult Create()
{
var model = new Accommodation
{
AustraliaStates = db.AustraliaStates
.ToList()
.Select(x => new SelectListItem
{
Text = x.AustraliaStateName,
Value = x.AustraliaStateId
})
};
return View(model);
}
/ POST: /Accommodation/Create
[Authorize]
[HttpPost]
public ActionResult Create(Accommodation accommodation, HttpPostedFileBase file1, HttpPostedFileBase file2, HttpPostedFileBase file3)
{
if (ModelState.IsValid)
{
// save and redirect
// ...blah blah...
//blah blah...
db.Accommodation.Add(accommodation);
//Save in Database
db.SaveChanges();
return RedirectToAction("Index");
}
// repopulate SelectList properties [I THINK THIS IS WRONG]
var model = new Accommodation
{
AustraliaStates = db.AustraliaStates
.ToList()
.Select(x => new SelectListItem
{
Text = x.AustraliaStateName,
Value = x.AustraliaStateId
})
};
return View(accommodation);
}
After filling the form, when save button is clicked, the following error message is displayed
The ViewData item that has the key 'SelectedAustraliaStateId' is of type 'System.String' but must be of type 'IEnumerable'.
In your HttpPost controller action you need to repopulate the correct property on the model that you are passing to the view, i.e. set the AustraliaStates on your model the same way you are doing in your GET action:
accommodation.AustraliaStates = db.AustraliaStates
.ToList()
.Select(x => new SelectListItem
{
Text = x.AustraliaStateName,
Value = x.AustraliaStateId
});
return View(accommodation);

ViewModel property doesn't bind into controller action parameter

My problem is that my ViewModel property doesn't bind into an action parameter.
I think it'll be more clear if i just give you my code.
I have a model as follows:
namespace Sima3.Models
{
using System;
using System.Collections.Generic;
public partial class Usuario
{
public string Login { get; set; }
public string NombreCompleto { get; set; }
public short Organigrama { get; set; }
public int Interno { get; set; }
public string EMail { get; set; }
}
}
And i added DataAnnotations to this partial class in another file (Because this model was generated automatically by EntityFramework), note the remote validation on Login property:
namespace Sima3.Models
{
[MetadataType(typeof(UsuarioMetaData))]
public partial class Usuario
{
}
public class UsuarioMetaData
{
[Display(Name = "Nombre de Usuario")]
[Remote("NoExisteUsuario", "Validation")]
[Required]
public string Login { get; set; }
[Display(Name = "Nombre y Apellido")]
[Required]
public string NombreCompleto { get; set; }
[Display(Name = "Sector")]
[Required]
public short Organigrama { get; set; }
[Required]
public int Interno { get; set; }
[EmailAddress]
[Required]
public string EMail { get; set; }
}
}
Now, i have a ViewModel wich contains a property of type Usuario and some other stuff needed to render my View:
namespace Sima3.ViewModels.PedidoViewModels
{
public class AgregarViewModel
{
public Usuario Usuario { get; set; }
public Pedido Pedido { get; set; }
public SelectList ListaSectores { get; set; }
public SelectList ListaEstados { get; set; }
public SelectList ListaPrioridades { get; set; }
public SelectList ListaTipos { get; set; }
public SelectList ListaDirecciones { get; set; }
}
}
And my view looks as follows (I'll only post part of it, if u need to see more let me know):
#using (Ajax.BeginForm("Crear", "Usuario", null,
new AjaxOptions
{
OnSuccess = "UsuarioCreadoSuccess",
HttpMethod = "Post"
}
, new { #class = "form-horizontal", id = "FormUsuario" }))
{
#Html.AntiForgeryToken()
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title title">Nuevo Usuario</h4>
</div>
<div class="modal-body">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Usuario.Login, new { #class = "col-lg-4 control-label" })
<div class="col-lg-7">
#Html.TextBoxFor(model => model.Usuario.Login, new { #class = "form-control"})
#Html.ValidationMessageFor(model => model.Usuario.Login)
</div>
</div>
Alright, the important part is the TextBoxFor, it renders me the next HTML:
<input class="form-control valid" data-val="true" data-val-remote="'Nombre de Usuario' is invalid." data-val-remote-additionalfields="*.Login" data-val-remote-url="/Validation/NoExisteUsuario" data-val-required="El campo Nombre de Usuario es obligatorio." id="Usuario_Login" name="Usuario.Login" type="text" value="">
As you see, the textbox name is: name="Usuario.Login"
And my controller action wich gets called by the Remote validation looks like this:
public JsonResult NoExisteUsuario([Bind(Prefix="Usuario")]string Login)
{
Usuario usuario = db.Usuarios.Find(Login);
if (usuario != null)
{
var MensajeDeError = string.Format("El usuario {0} ya existe", Login);
return Json(MensajeDeError, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
I set a breakpoint in this Action and it gets hit, but Login comes in null.
I checked with google chrome's debugger the http request header and it shows the form is getting submitted like this: Usuario.Login: asdasdasd.
The question is simple, how can i make it bind?
By the way, i'm using MVC5.
Thanks.
Well, i finally got it working, it seems i was setting the Prefix binding attribute wrongly.
Now my action looks like this:
public JsonResult NoExisteUsuario([Bind(Prefix="Usuario.Login")]string login)
{
Usuario usuario = db.Usuarios.Find(login);
if (usuario != null)
{
var MensajeDeError = string.Format("El usuario {0} ya existe", login);
return Json(MensajeDeError, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}

MVC4 dropdownlist error

Based on this post
Second answerI tried to create a dropdown list for my register page.
Register page has a field where you can select the PossibleAccessRight for the user while registering him/her which should be saves in AccessRight Attribute.
Right now i can't even show the items in dropdownlist
My model looks like this
public class UserModel
{
public int UserId { get; set; }
[Required]
[EmailAddress]
[StringLength(100)]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email ID ")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[StringLength(20,MinimumLength = 6)]
[Display(Name = "Password ")]
public string Password { get; set; }
[Required]
[Display(Name = "First Name ")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name ")]
public string LastName { get; set; }
[Required]
[Display(Name = "Address ")]
public string Address { get; set; }
public List<string> PossibleRights;
[Required]
[Display(Name = "Access Rights")]
public string AccessRight { get; set; }
public UserModel()
{
PossibleRights = new List<string>()
{
{"High"},
{"Low"},
};
}
}
in controller i have this in registeration method which is httppost method
[HttpGet]
public ActionResult Register()
{
return View();
}
[HttpPost]
public ActionResult Register(Models.UserModel user)
{
var rights = new UserModel();
if (ModelState.IsValid)
{
using (var db = new DBaseEntities())
{
var crypto = new SimpleCrypto.PBKDF2();
var encrpPass = crypto.Compute(user.Password);
var sysUser = db.SystemUsers.Create();
sysUser.FirstName = user.FirstName;
sysUser.Email = user.Email;
sysUser.Password = encrpPass;
sysUser.PasswordSalt = crypto.Salt;
db.SystemUsers.Add(sysUser);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("","Login data is incorrect.");
}
return View(rights);
}
View for this method looks like this
<div class="editor-label">#Html.LabelFor(u=> u.FirstName)</div>
<div class="editor-field"> #Html.TextBoxFor(u=> u.FirstName)</div>
<br/>
<div class="editor-label">#Html.LabelFor(u=> u.LastName)</div>
<div class="editor-field"> #Html.TextBoxFor(u=> u.LastName)</div>
<br/>
<div class="editor-label">#Html.LabelFor(u=> u.Address)</div>
<div class="editor-field"> #Html.TextBoxFor(u=> u.Address)</div>
<br/>
<div class="editor-label">#Html.LabelFor(u=> u.Email)</div>
<div class="editor-field"> #Html.TextBoxFor(u=> u.Email)</div>
<br/>
<div class="editor-label">#Html.LabelFor(u=> u.Password)</div>
<div class="editor-field"> #Html.PasswordFor(u=> u.Password)</div>
<br/>
<div class="editor-label">#Html.LabelFor(u=> u.AccessRight)</div>
<div class="editor-field"> #Html.DropDownListFor(u=> u.PossibleRights, new SelectList(Model.PossibleRights))</div>//error at this line(NullReference exception)
<br/>
<input type="submit" value="Register"/>
any idea what I'm doing wrong? Also, is my approach to show the items in dropdownlist good? Can you suggest better idea if any?
If you want to display any info on the view, you have to provide this info to the view first. Right now this code:
public ActionResult Register()
{
return View();
}
does not provide any info at all. Model for the view is created with default constructor, which means that model object is empty, therefore nothing is displayed on the view (particularly in the dropdown list). What you need is some initialization like this:
public ActionResult Register()
{
UserModel model = new UserModel();
model.PossibleRights = new List<string>{"Right1", "Right2", "Right3"};
// or go to db, or whatever
return View(model);
}
Besides dropdown returns selected value when posted, which is string representing a right in this case. So you need to introduce some field in the model to store the selection:
public class UserModel
{
...
public List<string> PossibleRights;
public string SelectedRight;
...
}
Usage on view is the following:
#Html.DropDownListFor(u => u.SelectedRight, new SelectList(Model.PossibleRights))

How to pass an entire ViewModel back to the controller

I have a ViewModel that contains two objects:
public class LookUpViewModel
{
public Searchable Searchable { get; set; }
public AddToSearchable AddToSearchable { get; set; }
}
The two contained models look something like this:
public class Searchable
{
[Key]
public int SearchableId { get; set; }
public virtual ICollection<AddToSearchable> AddedData { get; set; }
}
public class AddToSearchable
{
[Key]
public int AddToSearchableId { get; set;}
[Required]
public int SearchableId { get; set; }
[Required]
public String Data { get; set; }
[Required]
public virtual Searchable Searchable { get; set; }
}
I have a view that uses my LookUpViewModel and receives input to search for a SearchableId. If the Searchable object is found, a LookUpViewModel object is created and passed to the View. The view then displays editor fields for AddToSearchable.Data. Once submitted, I want the LookUpViewModel to be passed to an action method to handle all the back-end code. The only problem is, the LookUpViewModel passed to my action method contains a null reference to Searchable and a valid reference to AddToSearchable.. i.e. I'm missing half of my data.
Here's an example of what my view looks like:
#model HearingAidTrackingSystem.ViewModels.LookUpViewModel
#using (Html.BeginForm("LookUp", "Controller", "idStr", FormMethod.Post))
{
<input type="text" name="idStr" id="idStr"/>
<input type="submit" value="Search" />
}
#if (Model.Searchable != null && Model.AddToSearchable != null)
{
using (Html.BeginForm("AddMyStuff", "Controller"))
{
Html.HiddenFor(model => model.Searchable.SearchableId);
Html.HiddenFor(model => model.Searchable.AddedData);
Html.HiddenFor(model => model.AddToSearchable.AddToSearchableId);
Html.HiddenFor(model => model.AddToSearchable.SearchableId);
Html.HiddenFor(model => model.AddToSearchable.Searchable);
<div class="editor-field">
#Html.EditorFor(model => model.AddToSearchable.Data)
#Html.ValidationMessageFor(model => model.AddToSearchable.Data);
</div>
<input type="submit" value="Submit" />
}
}
and here are my action methods:
public ActionResult LookUp(LookUpViewModel vm)
{
return View(vm);
}
[HttpPost]
public ActionResult LookUp(string idStr)
{
int id = /*code to parse string to int goes here*/;
Searchable searchable = dal.GetById(id);
LookUpViewModel vm = new LookUpViewModel { Searchable = searchable,
AddToSearchable = new AddToSearchable() };
//When breakpoint is set, vm contains valid references
return View(vm);
}
[HttpPost]
public ActionResult AddMyStuff(LookUpViewModel vm)
{
//**Problem lies here**
//Do backend stuff
}
Sorry for the lengthy post. I tried my best to keep it simple. Any suggestions you may have.. fire away.
Two methods to fix it:
You can add to do HiddenFor() for all properties of Model.Searchable.
You can use serialization to transfer your Model.Searchable into text presentation and repair it from serialized form in controller.
Update: The problem is: You need to use #Html.HiddenFor(), not Html.HiddenFor();.

Resources