MVC validation with only HTML - asp.net-mvc

I am working on a contact us form and I was able to do a successful validation but i have a small problem,
when I submit and miss one of the required values, the form comes back with the error message and does not keep the value and I have noticed that only happens if I use HTML only, it will work fine with HTML helpers.
Here is a snippet of my code:
<div class="form-group">
#Html.LabelFor(model => model.FirstName, "First Name")
<div>
<input class="form-control" name="FirstName" id="FirstName" data-val="true" type="text" value="" />
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
But it will work for the last name field:
<div class="form-group">
#Html.LabelFor(model => model.LastName, "Last Name")
<div>
#Html.EditorFor(model => model.LastName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
</div>
Filling the info:
Notice after submitting the last name stays there but the first name disappears:
Any help will be appreciated :)

Because your input tag's value property value is set to an empty string.
You should use the Html.TextBoxFor helper method to generate the inuput field and it will show you the value you posted when your model validation fails. The helper method will also generate the relevant html5 attribute needed for client side unobtrusive validation.
#model ContactViewModel
#using(Html.BeginForm())
{
#Html.TextBoxFor(s=>s.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
<!-- Other fields -->
<input type="submit" />
}
Assumin your HttpPost action method is returning the posted model back to the view which Model validation fails
[HttpPost]
public ActionResult Contact(ContactViewModel model)
{
if(ModelState.IsValid)
{
// to do : return something
}
return View(model);
}

Related

How to validate the HTML controls with data annotations in MVC?

In .Net MVC. I have a html control. Inorder to bind it with the model property I am using name attribute. How do we get the validations(using data annotation) provided in the model class property into the html control?
In Cshtml
#using (Html.BeginForm("ClaimWarranty", "WarrentyClaim", FormMethod.Post, new{ enctype = "multipart/form-data" }))
{
<div class="form-group row">
<label for="" class="col-md-2 col-form-label input-label">Email Address:</label>
<div class="col-md-8">
<input type="text" name="Emailaddress" class="form-control input-style" placeholder="example#company.com">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" onclick="ValidateFileSize()" class="btn btn-default" />
</div>
</div>
}
//The model class is below;
public class ClaimWarranty
{
[Required(ErrorMessage = "Email ID is Required")]
[DataType(DataType.EmailAddress)]
[MaxLength(50)]
[RegularExpression(#"[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,4}", ErrorMessage = "Incorrect Email Format")]
public string Emailaddress { get; set; }
}
I am using the name property to bind the text box to the model property .
<input type="text" name="Emailaddress" class="form-control input-style" placeholder="example#company.com">
How do I get the validations in the html control ,provided in the model class (using the data annotations) as shown above without using jquery validations or razor code?
In View
#model Demo.Models.Student
#using (Html.BeginForm("SaveStudent", "Student", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
#Html.LabelFor(model =>model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model =>model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model =>model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btnbtn-primary" />
</div>
</div>
}
In Model
public class Student
{
[Required(ErrorMessage = "Please enter name"), MaxLength(30)]
public string Name { get; set; }
}
By default, ASP.Net MVC framework executes validation logic during model binding. In Controller side, we need to check
if (ModelState.IsValid)
{
}
OR We can also check Individual validation, as shown below:
if (ModelState.IsValidField("LastName") == false)
if(!ModelState.IsValid)
{
// you can get the error information from model state, convert it into list
var validationErrors = ModelState.Values.Where(E => E.Errors.Count > 0)
.SelectMany(E => E.Errors)
.Select(E => E.ErrorMessage)
.ToList();
// now you have got the list of errors, you will need to pass it to view
// you can use view model, viewbag etc
ViewBag.ErrorList = validationErrors;
return View();
}
else
{
// perform your business operation, save the data to database
return View();
}
On View Page -
you have to add check for validation error list
if(ViewBag.ErrorList != null)
{
foreach(var errorMessage in ViewBag.ErrorList)
{
// here you can display the error message and format in html
}
}
Way you can display error on view page
1. #Html.ValidationSummary() - It will display summary of the validation errors
2. #Html.ValidationMessageFor(x => x.Emailaddress) - It will display error message
for specific property
3. you have to manually retrieve the error information from model state and then store it in list and pass to the view page.

ASP.NET MVC - Determine if field has an error in Razor view

I'm working on an input form in ASP.NET MVC. My input form looks like this:
#using (Html.BeginForm("Result", "Contact", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role="form" }))
{
<h4>What do you want to tell us?</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group label-floating">
<label class="control-label" for="Subject">Subject</label>
<input class="form-control" id="Subject" name="Subject" type="text">
</div>
<div class="form-group">
<input type="submit" value="Send" class="btn btn-primary btn-raised" />
</div>
#Html.AntiForgeryToken()
}
My model behind this form looks like this:
public class ContactModel
{
[Required(ErrorMessage="Please enter the subject.")]
[Display(Name="Subject")]
public string Subject { get; set; }
}
I want to conditionally apply classes and structure based on whether or not the Model is valid. I also want to do it per field. My question is, in Razor, how do determine if the "Subject" property is valid, or if it has errors? Thanks!
While ValidationMessageFor is the standard approach for displaying validation errors, the following code does exactly what you asked for and can be useful in rare circumstances:
#if (!ViewData.ModelState.IsValidField("Subject"))
{
//not valid condition
}
As was said in comments and by #PeterB - for displaying validation messages per-input should be used Html.ValidationMessageFor method somewhere near with your input on a view. But I want to notice: you have a model but do not use it in your form. More of this, you have data annotations for client and server validation and labeling and don't use them on your view too.
Please check this approach for your view:
#model /*your.possible.namespace.*/ContactModel
...
#using (Html.BeginForm("Result", "Contact", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role="form" }))
{
<h4>What do you want to tell us?</h4>
<div class="form-group label-floating">
#Html.LabelFor(m => m.Subject, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Subject, new { #class = "form-control", #id = "Subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
}
This snippet should display the error message that you described in ErrorMessage property of Required data annotation after posting a form. You can find useful to enable an unobtrusive validation.
You can find an article about validation in ASP.NET MVC guides.
P.S.: And don't forget to perform a server-side validation in controller (for example).
In the Controller you can use ModelState.IsValid to see if there are errors, and also ModelState["nameOfField"] to see if a specific field has errors.
In the View you can use Html.ValidationSummary() (show all errors, if any) or Html.ValidationMessageFor() (show error for specific control, if any). Examples are plenty, both on & off StackOverflow.

Set Text Box value using drop-down list's selected value

I have a schema ( table saved in Sql) like this:
ID | Name | Address
1 Sam ADD1
2 John ADD2
3 Tony ADD3
I have three fields in my form - ID , Name and Address.
Now I have implemented a drop-down for the ID field using ViewBag to pass data between the view and the controller.
I want to set the value of the fields Name and Address using the ID selected from the drop-down and not manually but I am unable to figure out a way and tried searching but could not find any appropriate solution.
Thanks for the Help.
EDIT:
My Code So Far:
Model Has been generated using the table itself (Database First Approach)
Controller
public ActionResult Create()
{
ViewBag.codes = db.Users.ToList();
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Entity_Master entity_Master)
{
if (ModelState.IsValid)
{
db.Entity_Master.Add(entity_Master);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.codes = db.Users.ToList();
return View(entity_Master);
}
View
#Html.LabelFor(model => model.ID, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.DropDownListFor(model => model.ID, new SelectList(ViewBag.codes, "ID", "ID", "Select ID"))
#Html.ValidationMessageFor(model => model.ID, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
#Html.LabelFor(model => model.Address, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(model => model.Address, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Address, "", new { #class = "text-danger" })
Rendered Html
<select id="ID" name="ID">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
<input class="form-control text-box single-line" id="Name" name="Name" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="Name" data-valmsg-replace="true"></span>
<input class="form-control text-box single-line" id="Address" name="Address" type="text" value="" />
<span class="field-validation-valid text-danger" data-valmsg-for="Address" data-valmsg-replace="true"></span>
You need to listen to the change event on this dropdown, get the selected value and using the value, get the details ( Name & Address) from Id. You may make an ajax call to the server to get the data.
$(function(){
$("#ID").change(function(){
var v = $(this).val();
var url="#Url.Action("Details",Users")";
$.getJSON(url, { id : v }, function(data){
$("#Name").val(data.Name);
$("#Address").val(data.Address);
});
});
});
Now you need to have an action method which accepts the Id and return a JSON structure which has Name and Address property.
public ActionResult Details(int id)
{
var details = new { Name="Shyju", Address="5580 Twin lakes dr" };
//Hard coded for demo. You may replace it with data from db.
return Json(details, JsonRequestBehavior.AllowGet);
}
I have used Url.Action helper method to generate the proper relative url to the action method. This will work if your js code is inside the razor view. If your code is inside an external js file, you may use the solution explained in this post.

Does the name of parameter have to be model?

Hit a strange issue where my model is not binding and shows up on the controller as null.
I have a form doing a httppost. My breakpoint in the controller is hit and the parameter I expect to be my model is null.
Looking at some example code on another page that works, I copied and pasted it and the only difference was the name of the parameter was 'model' instead of message.
View
#model Site.Models.ContactMessage
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ContactMessage</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Message, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Message, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Message, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.To, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.To, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.To, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller
public ActionResult Contact()
{
return View();
}
[HttpPost]
public ActionResult Contact(ContactMessage message)
{
var m = message;
return View();
}
and it worked. I thought I must have entirely missed something about naming convention. Found you can use Bind, from reading a heap of other posts, to change the prefix like;
public ActionResult Contact([Bind(Prefix = "model")] ContactMessage message)
but that didn't work, still null. Going to rename it to model so it works and I can move on but would like to know why it's not binding if not called model.
public ActionResult Contact(ContactMessage message)
Changed back to this as above but still returns a null.
Interestingly, if I open up another MVC app, that one has whatever parameter names I want and works fine. It's using an older version of MVC 5 (not updated it yet but I will do that and see if anything happens. I don't expect it will.)
Your problem is that you model contains a property named Message and you also have a parameter named message The DefaultModelBinder reads the form values which will include message = "someTextValue" and searches for model properties that have the name message. It finds the one in you model and sets it value (all OK so far) but then it finds another one (your parameter) and tries to set the value of a complex object string value (in effect ContactMessage message = "someTextValue";) which fails so the model becomes null

ASP.NET MVC Persisting mdoel's ID value when Editing

public Edit(int? id){ /* Codes */ }
[HttpPost]
public Edit(Item model){ /* Codes */ }
I retrieve a copy of Item in the first Edit method, which would contain a value for ItemID. But when it gets to the HttpPost method, the id value's lost.
If switched to
public Edit(int? ItemID){ /* Codes */ }
[HttpPost]
public Edit(Item model){ /* Codes */ }
this way ItemID can be persisted in the Item model.
But is this a good way to handle it? Will ASP.NET MVC always be able to know that it needs to plug "ItemID" into Item?
and are there other ways to persist the ID value? Thanks.
I cannot understand how do you lose id at the HttpPost handling. Maybe you should check your binder and possibly write one for yourself? In my experience default binders are a little cumbersome. You could start from here although I don't pretend it's the best solution. In case you need to write many binders by hand take a look at some tools that could help you make conversion in declarative way like AutoMapper .
Have you tried adding the id as a parameter to the Post action?
public Edit(int? id){ /* Codes */ }
[HttpPost]
public Edit(int id, Item model){ /* Codes */ }
This way, when the form is posted back, the id will be populated from the URL.
Is the property on your Item model called ItemID? If so, then the default model binder won't populate it if you're passing around a field called ID. If you change your method signatures so that the parameter names match up with your Item property names it should work as expected.
Phil Haack had a post that may or may not be related to what you're doing.
Also, if you're not sending the ID out to the client as part of a form (hidden field or whatnot) and it isn't part of the POST URL then it would only make sense that you wouldn't have the ID field populated properly on POST.
The MVC 2 way of solving the ID issue is Optional URL Parameters.
If you're still on MVC 1 then use a binding attribute on the method argument:
[HttpPost]
public ActionResult Edit([Bind(Exclude = "ItemID")] Item model)
{
// ...
}
Little late answer but when using razor its common to use a hidden field in order to bind the Id to the model.
#Html.HiddenFor(model => model.Id)
A complete form post could look like this:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Address</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StreetLine1, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StreetLine1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StreetLine1, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}

Resources