How to display only one error message using Html.ValidationSummary? - asp.net-mvc-3

I have a view (skipping redundant code for simplicity):
#model CompetencyModel
#using (Html.BeginForm())
{
#Html.ValidationSummary()
#Html.EditorFor(model => model.Results)
}
When I submit, if validation fails inside EditorFor, ValidationSummary shows the list of all validation messages for each item coming from EditorFor.
This screenshot is a view, where EditorFor is a single line with radio buttons:
But I need a single error message just saying that something failed. I tried:
#Html.ValidationSummary(true)
#Html.EditorFor(model => model.Results)
But it doesn't show anything. I tried
#Html.ValidationSummary(true)
#Html.EditorFor(model => model.Results)
#Html.ValidationMessageFor(model => model.Results, "ERROR !!!")
But it doesn't show anything.
What am I doing wrong? Thank you.
Solution
This will do the trick.
<style>.validation-summary-errors ul { display: none; }</style>
#Html.ValidationSummary("Can't save, you must answer all questions !!")

The ValidationExtensions.ValidationSummary method will always return an un-ordered list of items.
Another person asked this question and choose to use JQuery to hide the items:
https://stackoverflow.com/a/10837702/1220302
Another solution you could do is only display the first item in the list using CSS's nth-child pseudo tag. This of course is dependent on the browser version you're using though:
I think something like this:
.validation-summary-errors ul li:nth-child(-n+1) {
display:none;
}

I think you can use the following rule to hide all messages except the first:
.validation-summary-errors ul li:nth-child(n+2) {
display:none;
}

Related

MVC 3 unobtrusive validation - conditionally disable/enable validation

I have a form that has an option to enter dimensions for:
Width & Height
Width
Height
And I have two container divs that I hide/show depending on which of the three options is selected:
<div class="editor-field" id="width-container">
#Html.EditorFor(model => model.Width)
#Html.ValidationMessageFor(model => model.Width)
</div>
<div class="editor-field" id="height-container">
#Html.EditorFor(model => model.Height)
#Html.ValidationMessageFor(model => model.Height)
</div>
If height is selected, then width is not displayed on the form, how can I disable the unobtrusive validation on the Width input field in a fashion that will allow me to easily re-instate it if the user changes their mind i.e. removing data-* attributes is not an option. I'm happy to create an CustomAttribute class to handle this BUT I do not want to have to hack the standard jquery files to make this work as it makes updating to new versions a headache down the track. If all else fails I'll use my usual trick of adding a value of 0 to the fields when they are not visible and then removing it when they are shown.
EDIT:
Please be mindful that when Width is not visible it is not a "hidden" field per se it's just a input tag that's not visible to the user because the parent div has a style of display:none
You can set up the jQuery validator that's processing your unobtrusive validation to ignore hidden elements:
jQuery.validator.defaults.ignore = ":hidden";
// the line above is outside any $(document).ready(...) or similar
$(document).ready(function(){
...
});
...
So it seems that this is the answer to my question (I went hunting again on Google hard to search for things that didn't relate to "hidden" fields):
https://stackoverflow.com/a/7673985/491950
e.g.
$("#height-container input[type='text']").attr("disabled", "disabled");
Thanks for your answers.

How to properly disable an Html Helper Textbox or TextBoxFor?

I have a razor display that is being used for entry. In one case, I would like the user to be able to populate the text box, in the other case, I would like to prevent the user from populating it. I am using code like:
#Html.TextBoxFor(model => model.Goop, new { #class = "text-box", maxlength = 2, onfocus = "javascript:this.select();" })
if (Model.Review.ReviewType.Equals("M"))
{
<script type="text/javascript">
$(function () {
$("#Goop").prop("disabled", true);
});
</script>
}
I have tried to do this several ways, jQuery (above), CSS attribs, javascript, ASP.NET... but all have the same issue: When the form is submitted, if the Goop textbox is disabled, the value for Goop in the model is Null. Ideas?
Thanks in advance!
Maybe it's not as cool without jQuery, but when I do this in my apps I do something along the lines of
if (Model.Review.ReviewType.Equals("M"))
{
#Html.DisplayFor(model => model.Goop)
#Html.HiddenFor(model => model.Goop)
}
else
{
#Html.TextBoxFor(model => model.Goop)
}
If a form element is disabled, it does not post a value. That's how it's supposed to work.
To work around this, you will need to do one of several things. You can enable the fields just before posting by intercepting the submit method. You can use a hidden field to store the data in addition to the disabled control. Or you can just assume the values on the controller side.
by the way, it should be .prop("disabled", "disabled"), which renders as disabled="disabled", that's standards compliant.

Rendering Partial Views in a Loop in MVC3

I have a pretty simple scenario, Model for my view is a List.
Loop through List like
#foreach(CustomObject obj in Model)
{
Html.Partial("_TrackingCustomObject",obj)
}
So i was expecting to have number of partial views according to my list.
Partial View has been developed accordingly.
There is no error on page. It just does not show any data that is supposed to display by partial views.
What is the reason of not showing any data?
You are missing an #:
#foreach(CustomObject obj in Model)
{
#Html.Partial("_TrackingCustomObject", obj)
}
But why writing foreach loops when you can use editor/display templates? Like this:
#model IEnumerable<CustomObject>
#Html.EditorForModel()
and then simply define the corresponding editor template (~/Views/Shared/EditorTemplates/CustomObject.cshtml) that will automatically be rendered for each element of your model:
#model CustomObject
<div>
#Html.EditorFor(x => x.Foo)
</div>
Simple and conventional :-)
You're missing the Razor symbol #:
#foreach(CustomObject obj in Model)
{
#Html.Partial("_TrackingCustomObject",obj)
}
Also make sure your partial view is using the object type CustomObject as the Model.
#model MyProject.Models.CustomObject
<h1>Yeah we're in a partial! #Model.SomeProperty </h1>
To try and drill down to where the error is, try placing some static text inside the PartialView.
<p>Some text</p>
If your collection has 10 items, then you should see 10 of these paragraphs. Next once this works, focus on displaying some property in each item.
#model MyProject.Models.CustomObject
<p>Some text</p>
<p>#Model.SomeProperty</p>
When you are creating html form using #Html.BeginForm() you have to wrap the remaining stuf inside a <div> or other container else the html elements won't get rendered.
Ex.
this won't work
#using(Html.BeginForm())
{
Html.EditorFor(m => m.Name)
}
this will work
#using(Html.BeginForm())
{
<div>
#Html.EditorFor(m => m.Name)
</div>
}
Bit late in the day, but this worked for me in MVC 4:
#foreach (var p in #Model.RelatedCards)
{
Html.RenderPartial("_ThumbPartial", p);
}
Try this:
#Html.RenderPartial("_TrackingCustomObject",obj)
This is too old but someone can use it.
#foreach(CustomObject obj in Model)
{
<text>
Html.Partial("_TrackingCustomObject",obj)
</text>
}

Validation messages not clearing in MVC 3.0 with unobtrusive jquery

I'm attempting to implement a simple client side validation in a web app I'm working on, and the actual validation message is working. However, when I correct the incorrect input and the control loses focus, the validation message doesn't clear and the invalid class remains on the control. Here's the relevant view code
#model Project.CommentViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<div class="Comment">
<div class="CommentInfo">
Post New Comment:
</div>
<div class="CommentText">
<div class="commentEdit ">
#Html.TextAreaFor(x => x.CommentText, new { #class = "NewCommentTextBox" })
#Html.ValidationMessageFor(x => x.CommentText)
</div>
#Html.HiddenFor(x => x.ProjectID)
</div>
</div>
}
And the view model attribute
[StringLength(50)]
public string CommentText { get; set; }
As I mentioned earlier, once the comment gets too long and the control loses focus, the error message comes up as expected. When the error is fixed however, the error message doesn't disappear and the control stays red. My _Layout page has the relevant script files included in the right order, and my config file has the appSetting variables set correctly. Any idea what's wrong or where I should be looking for the problem at? Thanks very much for any advice.
Resolved the problem. A Telerik grid that was present elsewhere on the page seems to have been conflicting with the validation somehow and broke it. Manually registering the jquery validation scripts with the grid resolved the issue.
#(Html.Telerik().ScriptRegistrar().DefaultGroup(group =>
group.Add("jquery.validate.js").Add("jquery.validate.unobtrusive.js")))
I think they may have resolved this particular issue with a newer version of the Telerik library.

MVC 3 Razor #Html.ValidationMessageFor not working in partial loaded via jquery.load()

I have put together a small example here just to replicate the problem.
I have a strongly typed partial view _Name.cshtml:
#model ValidationInPartial.ViewModels.MyViewModel
<h2>#ViewBag.Message</h2>
<fieldset>
<legend>Name</legend>
<div class="editor-label">
#Html.LabelFor(model => model.MyName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyName)
#Html.ValidationMessageFor(model => model.MyName)
</div>
Reload Name
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<script type="text/javascript">
$(document).ready(function () {
$("#reload").click(function () {
$("#divName").load("Home/NameReload");
});
});
</script>
that is initially loaded and displayed inside the main Index.cshtml
<div id="divForm">
#using (Html.BeginForm()) {
<div id="divName">
#Html.Partial("_Name")
</div>
}
</div>
The field MyName is required and validation is implemented through Required attribute in MyViewModel
namespace ValidationInPartial.ViewModels
{
public class MyViewModel
{
[Required(ErrorMessage = "Please enter a Name.")]
public string MyName { get; set; }
}
}
After the page is loaded the first time, if you click the Create button leaving the field empty the validation message "Please enter a Name." shows beside the field and the field itself turns pink, which is the expected behaviour.
Now by clicking the "Reload Name" link, which makes an ajax call (jquery.load(...)), the partial is reloaded, here is controller code:
public PartialViewResult NameReload()
{
MyViewModel myViewModel = new MyViewModel();
ViewBag.Message = "Name Reloaded";
return PartialView("_Name", myViewModel);
}
This time if you click the Create button leaving the field empty the validation message does not appear beside the field, although the field turns pink.
It turns out that when reloading the partial the #Html.ValidationMessageFor doesn't render the validation message as the first time.
Here is the jquery files I use
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
I wonder if this is a bug in the way the Razor engine renders the #Html.ValidationMessageFor or is that a problem with jquery?
Any idea why this happens?
I have also read somewhere that the ajax call looses all the scripts for the page, in fact I have to keep any javascript code inside the partial so that they can be rendered and used again.
In the meantime I found a workaround which is to manually render in the partial what was supposed to be rendered by #Html.ValidationMessageFor which is:
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="MyName"></span>
However this workaround means that if we change the type of validation or just the validation message inside the Required attribute in the ViewModel, we need to modify this hard-coded piece of html in the view.
#NickBork has a great answer here. The key is that ASP.NET's MVC rendering engine does not output the validation script if it doesn't think that there is a form. The example given hacks it buy putting in a form and then selection an inner section of HTML from was was returned, essentially throwing the outer wrapper of the form away.
There is another method so that you can just get your view:
ViewContext.FormContext = new FormContext();
With this method, there won't actually be FORM code output, but the validation markup will be there.
Thanks,
John
Validation markup (span tags, custom field attributes, etc) are not rendered unless your fields are contained within a FORM. The validation plugin itself does not work with elements outside of a form.
When ASP.NET renders your Partial View the controls are not within a form and thus do not get the elements rendered.
When you load you're partial content you'll need to parse the HTML using a jQuery selector.
In my sample below I have a TBODY on the parent View page that contains rows. When I need to add additional rows, I make a call to a View which had a form, table, tbody and collection of rows.
$.ajax({
type: "POST",
url: "/controller/action",
data: ({Your: 'dataHere'}),
dataType: "html",
success:
function(response){
$('tbody').append($('tbody',$(response)).html());
//The validation plugin can't bind to the same form twice.
//We need to remove existing validators
$('form').removeData("validator");
//Refresh the validators
$.validator.unobtrusive.parse(document);
},
error:
function(){
alert('An error occured while attempting to add the new content');
}
});
Note that I'm using a jQuery selector to select the rows that are inside of the View/PartialView that are loaded in by using AJAX:
$('tbody',$(response)).html()
The rest of the wrapper just appends the rows from the AJAX View/PartialView to the calling parents tbody:
$('tbody').append($('tbody',$(response)).html());
A couple other notes, after the validator plugin has been run on a form, it can not be called again without re-adding it (see jquery.validate.unobtrusive not working with dynamic injected elements)
To fix this, I first call the following method to remove all validators:
$('form').removeData("validator");
$("form").removeData("unobtrusiveValidation");
I then refresh the validators using the following:
$.validator.unobtrusive.parse(document);
I can't remember where I found the solution. The reason is because you are loading a PartialView into a View that has already been parsed by the jquery.validator.unobtrusive library. You need to re-parse the unobtrusive library
function ReparseValidation(){
jQuery.validator.unobtrusive.parse("#yourcontainer");
}

Resources