I'm trying Remote validation on MVC3 for an user name input text on a register form, everything work ok except that I'm expecting the remote validation trigger when the input lost focus but it is triggering on every key-press or key-up that is written in the input, resulting in many request to the action validation many as every character you write.
There are any way to change this behaviour using the build in unobtrusive javascript.
This is how my property model look like:
[Required]
[Display(Name = "User Name")]
[Remote("ValidateUniqueUserName", "Account")]
public string UserName { get; set; }
this is how the controller looklike:
public JsonResult ValidateUniqueUserName(string username)
{
if (string.IsNullOrEmpty(username))
{
return Json("User Name is required", JsonRequestBehavior.AllowGet);
}
var result = _membershipApplicationService.IsUserNameAvalible(username);
if (!result)
{
return Json("User Name is already taken, please try another one", JsonRequestBehavior.AllowGet);
}
return Json(true, JsonRequestBehavior.AllowGet);
}
And the register form is rendered using a #Html.Partial
thanks.
Related
I have a problem In association with validation property! I use .net core 2.1. I checked unique fields in OnModelCreating method in DbContext class and it's working fine. now I want to display an error message if the user enters the same BirthCertificate value (already exists in the database) in the input field like that Display & Required & MaxLength & ... attributes and send (bind) it to ModelState to check it. i also use jquery.validate.js in the client and show all errors and it's working fine. how I should do this:
Public Class Person
{
[Display(Name = "Enter BirthCertificate")]
[Required(ErrorMessage = "Please enter {0}")]
[MaxLength(10, ErrorMessage = "Max lenght is {0}")]
public string BirthCertificate { get; set; }
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<DomainClasses.Person.Person>(entity =>
{
entity.HasIndex(e => e.BirthCertificate).IsUnique(); // it's working fine
});
}
thanks
You can use either one of these.
1) Create new unique key for BirthCertificate in SQL Server. Use catch (Exception ex) and return to ajax to show error.
2) Do checking code
if (db.Person.Where(x => x.BirthCertificate.Contains(birthcert)).Any())
{
//return to ajax to show error
}
asp.net mvc server side validation when the javascript is disabled in the browser? i used "remote" in my modal class it validates only when the javascript is enabled it doesnt validate when the javascript is disabled.
Scenario for my problem is i have a table in my db with a column "code" with the datatype varchar. any one inserts the data they must insert the unique code.
Please do help me out
I would suggest to forget about remote because if you are using code first entity framework, you can't have more that one unique column in your table. I would just write code for it like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Insert a new user into the database
using (UsersContext db = new UsersContext())
{
UserProfile email = db.UserProfiles.FirstOrDefault(u => u.Email.ToLower() == model.Email.ToLower());
try
{
// Check if email already exists
if (email == null)
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { Email = model.Email });
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("Email", "Email address already exists. Please enter a different email address.");
}
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
}
Replace the email with the property you want to validate. At post, this will compare with entries with what already exists in your database, and depending on results, it will give you feedback. Throws exception if such data exists.
I've written a form in ASP.NET MVC3, and I can't get the entry to save the changes I make in the database, but while debugging, I noticed that the changes were reflected in the data context. I am experiencing no errors running this code. Let me know if you need more. Thanks!
Controller
[HttpPost]
public ActionResult Edit(Tool tool, FormCollection collection)
{
if (collection["Tool.Person.PersonID"] != "")
{
tool.Person= context.People.Find(
Convert.ToInt32(collection["Tool.Person.PersonID"])
);
}
if (collection["Tool.Company.CompanyID"] != "")
{
tool.Company = context.Companies.Find(
Convert.ToInt32(collection["Tool.Company.CompanyID"])
);
}
if (ModelState.IsValid)
{
context.Entry(tool).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(tool);
}
The first two if statements are checking to see if the user inputted a person or company, and the information is passed via the FormCollection. PersonID and CompanyID are primary keys for Person and Company, respectively. I went through the method line by line multiple times and achieve the same result - after context.SaveChanges();, the context reflects the changes, but the database entries remain null for both Person_PersonID and Company_CompanyID.
Try using a view model and accessing the database after the user submits the form.
This should get you well on your way.
ViewModel
using System.ComponentModel.DataAnnotations;
namespace Project.ViewModels
{
public class _tools
{
[Required(ErrorMessage="ToolID is required")]
public int32 ToolID{ get; set; } //whatever ID you use to retrieve the Tool from the database.
[Required(ErrorMessage="PersonID is required")]
public int32 PersonID{ get; set; }
[Required(ErrorMessage="CompanyID is required")]
public int32 CompanyID{ get; set; }
}
}
Controller Post
[HttpPost]
public ActionResult Edit(_tool viewModel)
{
if (ModelState.IsValid)
{
Tool tool = db.GetTool(viewModel.ToolID) //whatever method you use to get a current version of the row. You already do this before you send the data to the client, so just copy that code
tool.Person = viewModel.PersonID
tool.Company = viewModel.CompanyID
context.Entry(tool).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(tool);
}
View
#model = _tool
#using(Html.BeginForm("Edit", "ControllerNameHere", FormMethod.Post, null))
{
#Html.HiddenFor(model => model.ToolID)
#*Also add whatever inputs you use to get PersonID and CompanyID from the user.
Make sure to either use the #Html helpers or to give them names that correspond.
i.e. name one input PersonID and the other CompanyID*#
<input type="submit" value="Edit">
}
I am following Scott Gu's blog: here
In his Blog he talks about client and server side validation.
How does one validate if username has already been taken and display this as a validation error message to the user?
In Scott's blog, this would be the same as validating if Title is unique:
public class Dinner
{
public int DinnerID { get; set; }
[Required(ErrorMessage = "Please enter a Dinner Title")]
[StringLength(20, ErrorMessage = "Title is too long")]
public string Title { get; set; }
[Required(ErrorMessage = "Please enter the Date of the Dinner")]
public DateTime EventDate { get; set; }
[Required(ErrorMessage = "Please enter the location of the Dinner")]
[StringLength(30, ErrorMessage = "Address is too long")]
public string Address { get; set; }
[Required(ErrorMessage = "Please enter your email address")]
[RegularExpression(".+\\#.+\\..+", ErrorMessage = "Please enter a valid email address")]
public string HostedBy { get; set; }
public virtual ICollection<RSVP> RSVPs { get; set; }
}
My first guess is that somehow this is done within the Model Controller, here:
//
// POST: /Home/Create
[HttpPost]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
nerdDinners.Dinners.Add(dinner);
nerdDinners.SaveChanges();
return RedirectToAction("Index");
}
return View(dinner);
}
And because the Title is stored in a database server, this would be server side validation.
I know how to check if the Title is unique, but I do not know how to make the validation message appear in the View like it does using declaratives like [Required] or [StringLength()]. For example, here is how I can check for uniqueness:
[HttpPost]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
foreach (var existingDinner in nerdDinners.Dinners)
{
if(existingDinner.Title == dinner.Title)
{
**// TODO: display validation error message?**
}
}
nerdDinners.Dinners.Add(dinner);
nerdDinners.SaveChanges();
return RedirectToAction("Index");
}
return View(dinner);
}
Using my imagination, and a magic wand, I would want to create a new declarative called [TitleIsUnique] that performs like the other validation declaratives.
Thank you in advance for your assistance.
You could create a custom attribute as mentioned and use IValidateObject but I prefer to add my errors to the ModelState in one of the layers in my application.
For this you can use ModelState.AddModelError
If you use ModelState.AddModelError("Title", "Title must be unique"); it will add an error to the Title field.
If you use ModelState.AddModelError("*", "Title must be unique"); it will add a general error message for the page.
[HttpPost]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
if(nerdDinners.Dinners.Any(d => d.Title == dinner.Title))
{
ModelState.AddModelError("Title", "The title is not unique");
return View(dinner);
}
nerdDinners.Dinners.Add(dinner);
nerdDinners.SaveChanges();
return RedirectToAction("Index");
}
return View(dinner);
}
You are probably looking at implementing your own attribute derived from CustomAttribute. Take a look at this blog post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx - it shows how to validate uniqueness. In the post IValidatableObject interface is used to perform validation but you should be able to the same by creating CustomAttribute.
I am using strongly typed views in an MVC3 web app. I've noticed that when a form is submitted, the ViewModel that is passed to the controller only has values for properties that have form elements associated with them. For instance, the example below shows a simple confirmation View with a checkbox and a phone number that the user must confirm before proceeding. When the form is submitted to the controller action, the UserConfirmed property contains a value, but the PhoneNumber property is null.
Is there any way for the ViewModel to retain all of its values or do I have to repopulate the ViewModel properties that do not have form elements associated with them?
The View
#model WebMeterReplacement.ViewModels.Appointment.ScheduleConfirmationViewModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(false)
#Html.CheckBoxFor(model => model.UserConfirmed)
<span>Please confirm before proceeding</span>
<div>
Phone Number: #Model.PhoneNumber
</div>
<input type="submit" value="Confirm"/>
The Controller
[HttpPost]
public ActionResult ScheduleConfirmation(ScheduleConfirmationViewModel model)
{
if (model.UserConfirmed)
{
// add ViewModel data to repository
}
else
{
ModelState.AddModelError("ERROR", WebResources.strERROR_ConfirmSchedule);
}
return View(model);
}
Since your writing the phonenumber as output to the page it won't be automatically posted back (you've found out that part) What you can do is populate an hidden or read-only field with the phonenumber so that it will be posted back to your controller. An second option is to make a new call to your datasource and repopulate your object before saving it back to your datasource.
I generally POST back information like this in a hidden input. I personally use this heavily to pass data needed to return the user exactly where they where before pressing edit.
In your case it's as simple as
#model WebMeterReplacement.ViewModels.Appointment.ScheduleConfirmationViewModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(false)
#Html.CheckBoxFor(model => model.UserConfirmed)
<span>Please confirm before proceeding</span>
<div>
#Html.HiddenFor(m => m.PhoneNumber)
Phone Number: #Model.PhoneNumber
</div>
<input type="submit" value="Confirm"/>
For future reference:
If your passing complex objects back you need one hidden field per attribute (Hiddenfor does NOT iterate)
View
WRONG
#Html.HiddenFor(m => m.PagingData)
RIGHT
#Html.HiddenFor(m => m.PagingData.Count)
#Html.HiddenFor(m => m.PagingData.Skip)
#Html.HiddenFor(m => m.PagingData.PageSize)
Action
public HomeController(AViewModel Model)
{
PagingData PagingData = Model.PagingData;
Skip = PagingData.Skip;
}
If your passing Arrays you can do it like this
View
#if (Model.HiddenFields != null)
{
foreach (string HiddenField in Model.HiddenFields)
{
#Html.Hidden("HiddenFields", HiddenField)
}
}
Action
public HomeController(AViewModel Model)
{
String[] HiddenFields = Model.HiddenFields;
}
Well, the form will only POST elements that you have created. As you found out, simply writing the phone number out to the page will not suffice. The model binder can only bind those properties which exist in the posted data.
Generally you have a couple of options here:
1) You can create Input elements for all of the properties in your model, using visible elements (like a textbox) for those properties you want to edit, and hidden elements which should be posted back but have no UI
2) Post back a partial representation of your model (as you are doing now), read the entity back in from it's data source (I assume you're using some kind of data source, EF maybe) and then alter the properties of that entity with the ones from your form.
Both scenarios are common but it really depends on the complexity of your model.
I know this thread is a bit old, but thought I'd resurrect it to get feed back on my solution to this.
I'm in a similar situation where my objects are passed to a view, and the view may only display part of that object for editing. Obviously, when the controller receives the model back from the default model binder, and values not posted back become null.. and saving this means that a DB value becomes null just because it wasn't displayed/returned from a view.
I didn't like the idea of creating a model for each view. I know it's probably the right way... but I was looking for a reusable pattern that can be implemented fairly quickly.
See the "MergeWith" method... as this would be used to take a copy of the object from the database and merge it with the one returned from the view (posted back)
namespace SIP.Models
{
[Table("agents")]
public class Agent
{
[Key]
public int id { get; set; }
[Searchable]
[DisplayName("Name")]
[Column("name")]
[Required]
[StringLength(50, MinimumLength = 4)]
public string AgentName { get; set; }
[Searchable]
[DisplayName("Address")]
[Column("address")]
[DataType(DataType.MultilineText)]
public string Address { get; set; }
[DisplayName("Region")]
[Searchable]
[Column("region")]
[StringLength(50, MinimumLength = 3)]
public string Region { get; set; }
[DisplayName("Phone")]
[Column("phone")]
[StringLength(50, MinimumLength = 4)]
public string Phone { get; set; }
[DisplayName("Fax")]
[Column("fax")]
[StringLength(50, MinimumLength = 4)]
public string Fax { get; set; }
[DisplayName("Email")]
[RegularExpression(#"(\S)+", ErrorMessage = "White space is not allowed")]
[Column("email")]
[StringLength(50, MinimumLength = 4)]
public string Email { get; set; }
[DisplayName("Notes")]
[Column("notes")]
[DataType(DataType.MultilineText)]
public string Notes{ get; set; }
[DisplayName("Active")]
[Column("active")]
public bool Active { get; set; }
public override string ToString()
{
return AgentName;
}
public bool MergeWith(Agent a, string[] fields)
{
try
{
foreach (PropertyInfo pi in this.GetType().GetProperties())
{
foreach (string f in fields)
{
if (pi.Name == f && pi.Name.ToLower() != "id")
{
var newVal = a.GetType().GetProperty(f).GetValue(a,null);
pi.SetValue(this, newVal, null);
}
}
}
}
catch (Exception ex)
{
return false;
//todo: Log output to file...
}
return true;
}
}
}
And to use this in the controller.. you'd have something like..
[HttpPost]
public ActionResult Edit(Agent agent)
{
if (ModelState.IsValid)
{
Agent ag = db.Agents.Where(a => a.id == agent.id).ToList<Agent>().First<Agent>();
ag.MergeWith(agent, Request.Params.AllKeys);
db.Entry(ag).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(agent);
}
This way, during post back, it takes the object from the database, and updates it with object from view... but only updates the values that were posted back.. So if you have a field like "address" or something that doesn't appear in the view.. it doesn't get touched during the update.
I've tested this so far and i works for my purposes, tho i welcome any feedback as I'm keen to see how others have overcome this situation. It's a first version and i'm sure it can be implemented better like through an extension method or something.. but for now the MergeWith can be copy/pasted to each model object.
Yes, Just place hidden fields in the form for those values which you are not using and want to return to server control.
Thanks