MVC3 add Field name in Error to Validation Summary - c#

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.

Related

ASP.NET MVC Localizing or Changing Default Model Binding Error Messages

How to change the language for "The value 'some value' is not valid for 'some property'" validation error?
Can anyone help? I want to translate the error in the picture to Russian Error. I read much sites, try to use RegularExpression, but it doesn't help
may be I don't correctly understand how to do that?
I need only translate the error, not need to change the culture.
In web.config:
<globalization culture="en" uiCulture="en" />
My entity with data annotations attributes:
public class Player
{
/* Some other properties */
[Required(ErrorMessage = "Укажите среднее количество блокшотов")]
[Range(0, 10.0, ErrorMessage = "Недопустимое значение, до 10")]
public float BlockPerGame { get; set; }
/* Some other properties */
}
My View:
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.Id)
<div class="box-form">
/* Some other properties */
<div class="text-style-roboto form-group">
<label>Среднее количество блокшотов</label>
#Html.TextBoxFor(m => m.BlockPerGame, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.BlockPerGame)
</div>
/* Some other properties */
<div class="form-group">
<button type="submit" class="button button-create" id="button-create">Добавить</button>
#Html.ActionLink("Отмена", "Index", null, new { #class = "button button-cancel", id = "button-cancel" })
</div>
</div>
}
And my Controller:
public class AdminController : Controller
{
/*Some other methods*/
[HttpPost]
public async Task<ActionResult> Edit(Player player, string ChoosingTeam)
{
if (ModelState.IsValid)
{
if (ChoosingTeam != string.Empty)
{
try
{
player.TeamId = int.Parse(ChoosingTeam);
await repository.SavePlayerAsync(player);
TempData["message"] = string.Format("Игрок {0} {1} сохранены", player.Name, player.Surname);
return RedirectToAction("Index");
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
}
}
IEnumerable<SelectListItem> list = new SelectList(repository.Teams, "Id ", "Name");
ViewBag.ChoosingTeamName = list;
return View(player);
}
}
When you enter an invalid value for a property, if model binder cannot bind that value to the property, the model binder sets an error message for that property. It's different from data-annotations model validation. It's in fact model binder validation error.
Localizing or Changing Default Model Binding Error Messages
Model binding error messages are different from model validation messages. To customize or localize them, you need to create a global resource and register it in Application_Start for DefaultModelBinder.ResourceClassKey.
To do so, follow these steps:
Go to Solution Explorer
Right click on project → Add → ASP.NET Folder → Choose App_GlobalResources
Right click on App_GlobalResources → Choose Add New Item
Choose Resource File and set the name to ErrorMessages.resx
In the resource fields, add the following keys and values and save the file:
PropertyValueInvalid: The value '{0}' is not valid for {1}.
PropertyValueRequired: A value is required.
Note: If you want to just customize the messages, you don't need any language-specific resource, just write custom messages in the ErrorMessages.resx and skip next step.
If you want localization, for each culture, copy the resource file and paste it in the same folder and rename it to ErrorMessages.xx-XX.resx. Instead of xx-XX use the culture identifier, for example fa-IR for Persian language
and enter translation for those messages, for example for ErrorMessages.fa-IR.resx:
PropertyValueInvalid: مقدار '{0}' برای '{1}' معتبر نمی باشد.
PropertyValueRequired: وارد کردن مقدار الزامی است.
Open Global.asax and in Application_Start, paste the code:
DefaultModelBinder.ResourceClassKey = "ErrorMessages";
ASP.NET CORE
For ASP.NET Core read this post: ASP.NET Core Model Binding Error Messages Localization.

Implementing IDisposable for #using in razor

This is a question related to this post (which is also made by me)
All the explanation of the website I'm creating is in the link above. Please see it there.
That being said, I'm trying to have two models of different types within a razor, and I'm having difficulty properly dealing with the directives, (#using #inherits, and all that)
and I get errors saying it failed to bind my custom made model to the one that is created by the CMS (UmbracoTemplateView).
In the course of thinking how to circumvent this, I came up with assigning #using directive for IPublishedContent, thus something like below.
This way, the UmbracoTemplateView should be assigned to a variable with a much narrower scope, meaningI should be able to avoid triggering that error.
#model CodeShare.Library.Models.SearchViewModel
#using CodeShare.Web.Controllers
<div class="carousel-inner">
<div class="carousel-item active">
<!--this part uses UmbracoTemplatePage to render the content dynamically-->
#using (UmbracoTemplatePage)
{
var pageSize = 5;
IEnumerable<IPublishedContent> newsPosts = Umbraco.AssignedContentItem.DescendantOrSelf("news").Children.Where(x => x.IsVisible() && x.DocumentTypeAlias == "newsPost").OrderByDescending(x => x.UpdateDate);
var page = 1; int.TryParse(Request.QueryString["p"], out page);
var totalPages = (int)Math.Ceiling((double)newsPosts.Count() / (double)pageSize);
if (page > totalPages)
{
page = totalPages;
}
else if (page < 1)
{
page = 1;
}
}
#foreach (var item in newsPosts.Skip((page - 1) * pageSize).Take(pageSize))
{
var imagid = item.GetPropertyValue("image");
string imgurl = Umbraco.Media(imagid.ToString()).Url;
<a href="#item.Url" class="media">
<img src="#imgurl">
<div class="media-body">
<h5 class="mt-0">#item.Name</h5>
<span>最終更新日: #item.UpdateDate.ToString("yyyy/MM/dd") </span>
</div>
</a>
}
</div>
From here I want to use the custom model, because the input will made by the user not in the backoffice.
in my understanding, with this beginform method below, it sends those HiddenFor's as a set of parameters to the method called "SubmitSearchForm" in a controller class called "SurfaceController"
#using (Ajax.BeginForm("SubmitSearchForm", "Surface", new AjaxOptions()
{
UpdateTargetId = "carousel",
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
}))
#Html.HiddenFor(m => m.DocTypeAliases)
#Html.HiddenFor(m => m.FieldPropertyAliases)
#Html.HiddenFor(m => m.PageSize)
#Html.HiddenFor(m => m.PagingGroupSize)
#Html.TextBoxFor(m => m.SearchTerm)
}
<button id="submit-button">Search</button>
<div class="carousel">
#{ Html.RenderAction("RenderSearchResults", "Surface", new { Model = Model.SearchResults });}
</div>
}
The only change from the one I posted on our.umbraco is #using directive when trying to render from the model, which is of "UmbracoTemplateView".
Here, I'm receiving an error saying the directive that can be assigned to the #using directive here must be of the type "IDisposable" and UmbracoTemplateView is not.
I understand what is meant by the error:
UmbracoTemplateView is not a native type from .NET thus needs to be clearly specified when it can be disposed of to signal at the garbage collector. How can I assign this interface?
========================================================================
added on 18/10/2018
I separated these two views in different cshtml's but I'm having the same problem at a higher level (should I call this node)
So when I display the page, it will be delegated like
"WebBase.cshtml"=>"Home.cshtml" => "Parts that need UmbracoTemplateView(in cshtml)" & "Parts that need custom model(in cshtml)" ....
//I get an error when I put the following two in the same cshtml
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#model CodeShare.Library.Models.SearchViewModel
#using CodeShare.Web.Controllers
#using ClientDependency.Core.Mvc
#{
Layout = "WebBase.cshtml";
}
#* the fun starts here *#
#{Html.RenderPartial("~/Views/Partials/Home/.....cshtml");}
#{Html.RenderPartial("~/Views/Partials/Home/....cshtml");}
#{Html.RenderPartial("~/Views/Partials/Home/.....cshtml");}
<!-- only this "news" part requires custom model by Mr.Paul -->
#{Html.RenderPartial("~/Views/Partials/Home/_News.cshtml");}
#{Html.RenderPartial("~/Views/Partials/Home/....cshtml");}
As such at some point I am going to need two models of different types in one place anyway

Get value from ModelState with key name

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");

Checking validation in view

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>
}

Html.TextBoxFor not using value of new Model object

I am working on my first MVC Webapplication (using Razor and C#) and I have run across a strange behaviour.
I am editing a "line" of data and using ajax calls to submit and redisplay data. Everything works fine as far as changing existing data and storing it goes. Also if I just redisplay the "line" that was submitted no problems.
However, I want to display a "new" line with some of the values from the old line retained and the rest blanked out.
However, when I submit the new line object to the Partial View, the "blanked" out values aren't being picked up by the #Html.... helpers. But if I display the property of the Model directly it has the correct (blank) value.
Here are the relevant sections of my code:
Controller Method:
[HttpPost]
public ActionResult EditLineForm(SkuRequestLine ln)
{
SkuRequestLine newline = null;
try
{
if (ln.Store(true))
{
ViewData["prodcatdesc"] = DataConnection.GetProductCategory(ln.Category).description;
newline = new SkuRequestLine();
newline.Copy(ln);
newline.Line = DataConnection.NextSkuRequestLineNumber(ln.Request);
newline.Comments = "";
newline.Description = "";
newline.Vendorsku = "";
return PartialView("EditLineForm", newline); // this line is being executed.
}
else
{
return PartialView("EditLineForm", ln);
}
}
catch (Exception ex)
{
List<string> msgs = new List<string>();
while (ex != null)
{
msgs.Add(ex.Message);
ex = ex.InnerException;
}
return PartialView("EditLineForm", ln);
}
}
Razor Code:
#model Sku_Management.Models.SkuRequestLine
#using (Ajax.BeginForm("EditLineForm", "SkuRequest", new AjaxOptions { OnSuccess = "UpdateLineList" }))
{
.
.
.
<tr>
<td>
<span class="editor-label">
#Html.LabelFor(model => model.Description)
</span>
</td>
<td colspan="5">
<span class="editor-field">
#Html.TextBoxFor(model => model.Description, new { #class = "fortywide" }) // Displays the Description from the edited Line passed in. Not what what Model.Description is.
#Html.ValidationMessageFor(model => model.Description)
</span>
<span>|#Model.Description|</span> // Displays "||" which is what it should be since Model.Description is blank.
</td>
</tr>
The only thing I can think of is that model => model.Description is using a cached version of the Model not the new Model passed into the PartialView call.
I have spent the day searching for anything even similar on the web but I can't find anything that even begins to describe this behavior.
Has anyone else encountered this and knows what I am dong wrong?
Thanks
This is because the HTMLHelpers look to the ModelState for values before using the Model.
You'll have to clear the ModelState entries to get this to work.

Categories

Resources