I am adding some error messages to my ModelState from controller so that I can display it in my view. My Code is like this
ModelState.AddModelError(key: "MyError", errorMessage: "This phone number is already in use. Please register with a different phone number.");
And in my view I am displaying it like this
foreach (ModelState modelState in ViewData.ModelState.Values)
{
var errors = modelState.Errors;
if (errors.Any())
{
foreach (ModelError error in errors)
{
<p class="common-error">#error.ErrorMessage</p>
}
}
}
One issue with this approach is that, It is displaying all kind of ModelState errors where I want only to show error messages with a key MyError. how can I make this?
You can iterate through keys like this:
foreach (var modelStateKey in ViewData.ModelState.Keys)
{
//decide if you want to show it or not...
//...
var value = ViewData.ModelState[modelStateKey];
foreach (var error in value.Errors)
{
//present it
//...
}
}
You can add a #Html.ValidationSummary(true, "", new { #class = "some_error_class" }) to show the validations messages from the model state automatically.
You can add it just after the #Html.BeginForm({...}) { call, wrapped in a div.
The true parameter will show the control errors as well, set it to false to show only errors defined like this: ModelState.AddModelError("", "My custom error message");
Related
I am trying to add error on my own using ModelState.AddModelError, and when I try to display it on view, validation span is in html, but there is no message.
Controller
if (!paymentTypeId.HasValue || !shipmentTypeId.HasValue)
{
var model = new CheckoutViewModel()
{
ShipmentTypes = m_ShipmentTypeService.GetAllShipmentTypes(true),
PaymentTypes = m_PaymentTypeService.GetAllPaymentTypes(true),
};
SetBaseProperties(model, null);
ModelState.AddModelError("ErrorCheckout", "L_CHOOSE_TYPE_ERROR");
return View(model);
}
View
#Html.ValidationMessage("ErrorCheckout", new { #class = "text-danger" })
On other pages I did same and it worket, I don't know what is problem here
Problem was in sending request to Action via ajax. When I changed to Html.Form and placed submit, it started working, strange behavior, but here you go.
This question continues from a previous question I posted at the link below:
Passing a filename to a view
In summary: I am trying to delete a file. I now have all the necessary functionality working thanks to the answer at the above post. However, occasionally, if the file was in use (which would happen if I downloaded the file prior to deleting for example), I would get an IOException.
I want to handle this exception and display a message back to the user when this happens.
I tried debugging the jQuery code, but whenever I put a breakpoint on it, I start skipping through 1000's of lines of jQuery libraries. So as a quick alternative I just put alerts everywhere.
I discovered that most of this code is not being executed - so I put comments beside where I think I should be seeing messages but am not. So as a result I am unable to debug or figure out what the code is supposed to be doing.
So my first question is how to get the exception showing. It seems that even though this is Ajax, the whole page still refreshes, which to me is not the expected behaviour for Ajax calls (so if an error is showing its maybe lost when the page refreshes). However, with all the alerts, I should see the error somewhere, but I am not. I have purposefully altered the working code to always throw an exception for now. Again, the delete functionality works, its the error reporting that fails.
My next question is to have a brief explanation of why each section of the code (where highlighted) is needed as I don't know why those sections exist and I cant figure it out because I cant debug into or show alerts for them.
Below is the index view and the code in question:
#model IEnumerable<FileInfo>
#{
ViewBag.Title = "File List";
}
<h2>Index</h2>
<p>#Html.ActionLink("Upload", "Upload")</p>
<p>#Html.ValidationSummary(true, "", new { #class = "text-danger" })</p>
<p>#Html.ValidationMessage("Name", new { #class = "text-danger" })</p>
<span class="message text-danger"></span>
<table class="table">
<tr>
<th>File Name</th>
<th>Actions</th>
</tr>
#foreach (FileInfo file in Model)
{
<tr>
<td>#file.Name</td>
<td>
<form class="deleteform">
#Html.AntiForgeryToken()
<input type="hidden" name="fileName" value="#file.Name" />
<input type="submit" value="delete" />
</form>
</td>
<td>#Html.ActionLink("Download", "Download", new { fileName = file.Name })</td>
</tr>
}
</table>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(document).ready()
{
var url = '#Url.Action("Delete", "FileManagement")';
$('.deleteform').submit(function ()
{
alert(".deleteform.submit entered...");//This alert shows
return confirm("are you sure ...");//This alert shows
var formData = $(this).serialize();
alert(formData);//This does NOT show.
var row = $(this).closest('tr');
alert(row);//This does NOT show.
$.post(url, formData, function (response)
{
alert("$.post() entered...");//This does NOT show.
alert(response);//This does NOT show.
if (response)
{
alert("response true");//This does NOT show.
alert(response);//This does NOT show.
//row.remove(); //This code actually works even though the alert above does not show.
} else
{
alert("response false");//This does NOT show. - I dont know what this section of code is for.
//alert("Error 1 - display message");
// Oops - display message?
}
alert("$.post() finished...");//This does NOT show.
}).fail(function (response)
{
alert("$.fail() entered...");//This does NOT show. - I dont know what this section of code is for.
alert("Error 2 - display another message");//This does NOT show.
// Oops
alert("$.fail() finished...");//This does NOT show.
});
return false; // cancel the default submit
alert(".deleteform.submit finished...");//This does NOT show.
});
}
</script>
}
Below is the controller for this view and the delete actions:
public ActionResult Index()
{
DirectoryInfo dirInfo = new DirectoryInfo(Server.MapPath("~/UserFiles"));
List<FileInfo> files = dirInfo.GetFiles().ToList();
return View(files);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(string fileName)
{
var path = Path.Combine(Server.MapPath("~/UserFiles"), fileName);
if (System.IO.File.Exists(path))
{
try
{
//System.IO.File.Delete(path);
throw new IOException("Hello - Test Message...");
}
catch (IOException e)
{
Response.StatusCode = 500;
Response.StatusDescription = e.Message;
//ModelState.AddModelError("Name", e.Message);
}
}
else
return Json(null);
//return HttpNotFound();
return Json(true);
//return RedirectToAction("Index");
}
I would appreciate any help on this.
You cannot use return confirm("are you sure ..."); in the .submit() handler because it either returns false which cancels everything, or it returns true in which case you will be making a normal submit. In either case, it exits the function.
You need to change the script to
$('.deleteform').submit(function () {
if (confirm("....") {
// make you ajax call here
}
return false;
});
You also need to modify your controller code. Currently your last line (return Json(true);) can never be executed, and the code in the if block is always returning an error, so will always go to the .fail() function. Generally, you should not return the specific details of exceptions your code throws (this just exposes it to malicious users) and it is better to return a more general error, or return null and hard code the error message in the script. There are various ways you can handle this, including
try
{
// delete the file
return Json(true); // to indicate success
}
catch (.... )
{
return Json(null); // indicate failure
}
which in the script means
if (response) { // this will be executed if you returned true
... // delete the row
} else { // this will be executed if you returned null
... // display a message to the user
}
and the .fail() function will be executed if an exception is throw on the server which you have not caught.
Alternatively you could return on object with properties for Success and Message which gives you a bit more control over the message, for example
return Json(new { Success = true });
// or
return Json(new { Success = false, Message = "The file does not exist"});
// or
retrn Json(new { Success = false, Mesage = "The file is in use, please try again late" });
and the in the script
$.post(url, formData, function (response) {
if (response.Success == 'False') {
var message = response.Message; // display it
I render a Validation Summary in my MVC 5 App as below:
#Html.Partial("_ValidationSummary", #ViewData.ModelState)
and the Partial View code is as below:
#model ModelStateDictionary
<div class="#(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors") panel panel-danger"
data-valmsg-summary="true">
<div class="panel-heading">
Please, correct the following errors:
</div>
<div class="panel-body">
<ul>
#foreach (var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
{
<li>#modelError</li>
<li>#modelError.ErrorMessage</li>
}
</ul>
</div>
</div>
This is working quite nicely - in that the error messages display for fields that are in error.
However - The name of the Field on the model that is error is not added - so I have a field for mobile number and for home phone and for work phone. If i put abc into each field then in my validation summary I get 3 error messages saying "Phone Number is Invalid" but I would like to add the field to the error message so that I can add that to the error message to the User will know which of the fields is in error?
Is there an easy way to tweak my current code to achieve this?
You can use the ModelMetaData, found in the ViewData, to retrieve the display name of the key :
#foreach (var key in ViewData.ModelState.Keys)
{
var modelState = ViewData.ModelState[key];
var property = ViewData.ModelMetadata.Properties.FirstOrDefault(p => p.PropertyName == key);
if (property != null)
{
var displayName = property.DisplayName;
foreach (var error in modelState.Errors)
{
<li>#displayName: #error.ErrorMessage</li>
}
}
else
{
foreach (var error in modelState.Errors)
{
<li>#error.ErrorMessage</li>
}
}
}
Make sure you add
#using System.Linq
at the top of the view.
You can also use a <label> to allow the user to click on the display name to automatically focus the input field:
<li><label for="#key">#displayName</label>: #error.ErrorMessage</li>
You can try below LINQ query
foreach (var modEr in ModelState.Select(st => new { Field = st.Key, Errors = st.Value.Errors.Select(er => er.ErrorMessage) }))
{
//modEr.Field //Field name
//modEr.Errors //List of errors
}
Additionally there is a MVC defined HTML helper to show validation summary, refer
http://msdn.microsoft.com/en-us/library/system.web.mvc.html.validationextensions.validationsummary(v=vs.118).aspx
You can just use data annotations against each of the fields in your model.
I am working with an MVC application.
My code lies like the following:-
if(//there are errors then display validation summary)
{
<div class="error">#Html.ValidationSummary(true, "There was an error!")<div>
}
#using (Html.BeginForm("FoundSimilarAccounts", "Account", FormMethod.Post, new { id = "contactform" }))
{
}
I just want to know what can be the if condition.
I only want to display that when their would be an error or their is something in the validation summary.
Thanks
You can inspect the IsValid property of the ModelState of the ViewData:
#if(!ViewData.ModelState.IsValid)
{
<div class="error">#Html.ValidationSummary(true, "There was an error!")<div>
}
Edit.aspx:
<th>Calendar<input id="datepicker" name="datepicker" type="text" class="input-box"/></th>
Controller action:
// POST: /Studenti/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int[] rb, int id, string datepicker)
{
List<nastava_prisustvo> nastava = new List<nastava_prisustvo>();
if (String.IsNullOrEmpty(datepicker))
ModelState.AddModelError("datepicker", "First name is required");
try
{
if (ModelState.IsValid)
{
string poruka = "";
for (int i = 1; i <= rb.Length; i++)
{
string name = "chk" + i;
string selID = Request.Form[name];
if (selID == "on")
{
nastava.Add(new nastava_prisustvo
{
br_indexa = int.Parse(Request.Form["id_stud" + i]),
id_predmet = id,
datum = System.DateTime.Parse(Request.Form["datepicker"])
});
}
}
return View("show", nastava);
}
}
catch(Exception ex){
ModelState.AddModelError("*", "An unexpected error occurred.");
}
return View("show", nastava);
}
}
How to validate datepicker fiel? How stop posting data if date is not selected and show appropriate message. I use ASP>NET MVC 1 and read this http://www.superexpert.com/Blog/archive/2008/09/09/asp-net-mvc-tip-42-use-the-validation-application-block.aspx but did not solve my problem
I would stick to server side validation. Try this:
DateTime datePosted;
if (!DateTime.TryParse(Request.Form["datepicker"], out datePosted))
{
this.ModelState.AddModelError("datepicker", "Invalid Date!");
}
if (this.ModelState.IsValid)
{
return View("show", nastava);
}
else
{
// return your GET edit action here
return Edit(5);
}
Your Edit view will automatically be passed any validation errors and you can display them with a validation summary.
<%= Html.ValidationSummary() %>
If you want to validate the date before posting back you're going to have to use client side validation. xVal is a popular plugin for this.
For server side validation you're going to want to look at model binding and model validation.
ASP.NET MVC does not use the same sort of controls you're used to using in ASP.NET. Basically you have to do most things manually in JavaScript code, whereas in ASP.NET it generates that code automatically.
I'd use JQuery or something to validate the data in the control on the button click.
since you have validate on the client with your own Javascript, try the jQuery UI data picker:
http://jqueryui.com/demos/datepicker/
Then, most simply, on the server side, you can take the string value "datepicker" Convert it in a try/catch.
var dtDatePicker = new DateTime();
try
{
dtDatePicker = Convert.ToDateTime(datepicker);
// worked, carry on
}
catch
{
// didn't work, invalid date.
}