Add Validation Message To View - asp.net-mvc

Lets say I have a form with no model binding or data annotations. When the form is posted how can I return the view with a validation message beneath the control - Note I'm trying to do server side validation here?
Below is a kind of example.
<input name="Address" type="text" value="">
<span class="field-validation-valid" data-valmsg-for="Address" data-valmsg-replace="true"></span>
public ActionResult Create(FormCollection collection)
{
if (string.IsNullOrEmpty(collection["Address"])
{
// Set the field validation error span message
ModelState.AddModelError("Address", "This field is required.");
return View();
}
}
Note: I know how add validation using a view model and data annotations. In this scenario I'm unable to use a view model so need some way to manually validate and return the validation messages back to the view.
The above doesn't seem to work
* Update *
Perhaps using viewData as follows:
<span class="field-validation-valid" data-valmsg-for="Address" data-valmsg-replace="true">#ViewData["Address"]</span>

I would go with html helpers.
#Html.ValidationMessage("Address")
This will automatically generate the HTML:
<span class="field-validation-valid" data-valmsg-for="address" data-valmsg-replace="true"></span>`
Your code looks correct.
if (string.IsNullOrEmpty(collection["Address"]))
{
// Set the field validation error span message
ModelState.AddModelError("Address", "This field is required.");
return View();
}

Related

Build list of data validation attributes for a given element

When using any of the Input Extension Helper Methods, like #Html.TextboxFor, any Validation Attributes from your model are automatically generated by the Razor engine (via ClientValidationEnabled/UnobtrusiveJavaScriptEnabled).
For example, take the following case which works fine
Model:
[Required]
public string QuestionOne { get; set; }
View:
#Html.TextBoxFor(model => model.QuestionOne)
#Html.ValidationMessageFor(model => model.QuestionOne)
Generated Markup:
<input type="text" id="QuestionOne" name="QuestionOne" value=""
data-val="true" data-val-required="The QuestionOne field is required." >
<span class="field-validation-valid" data-valmsg-for="QuestionOne" data-valmsg-replace="true"></span>
In this case the attributes data-val="true" & data-val-required="The QuestionOne field is required." are picked up by Unobtrusive validation and the form element is successfully validated.
However, for extensibility reasons, I want to be able to generate the <input> element myself instead of using TextBoxFor. So my view would now look like this:
<input type="textbox"
id="#Html.IdFor(m => m.QuestionTwo)"
name="#Html.NameFor(m => m.QuestionTwo)"
value="#Model.QuestionTwo"
data-val="true" data-val-required="Selection is Required" />
#Html.ValidationMessageFor(model => model.QuestionTwo)
In this case, I'm faking the validation attribute output by just re-writing data-val="true" (etc) by hand, but this would have to be expanded to cover every single case.
Here's a running Demo in .NET Fiddle
Q: Can I build /return a list of data-val-* attributes for a given element?
You can use the GetUnobtrusiveValidationAttributes() method of HtmlHelper to get the validation attributes associated with a specific property.
For example in the view
#{ var attributes = Html.GetUnobtrusiveValidationAttributes("QuestionTwo"); }
<input
type="textbox"
#foreach(var attr in attributes)
{
#:#attr.Key="#attr.Value"
}
id="#Html.IdFor(m => m.QuestionTwo)"
....
/>
Note the #:#attr.Key="#attr.Value" line will give a warning (Missing attribute name) but will run correctly
Alternatively, you could use javaScript/jQuery to add the attributes
<script type="text/javascript">
var attributes = #Html.Raw(Json.Encode(attributes));
var input = $('#QuestionTwo');
for(var i in attributes) {
input.attr(i, attributes[i]);
}
</script>
I have forked the DotNetFiddle here to show the working code for both options.
While the above code shows how it can be done, you should not be doing that. The HtmlHelper methods execute a lot of code your ignoring to ensure correct 2-way model binding, for example, the value attribute is determined by first checking for a value in ModelState, then in the ViewDataDictionary, and only if the previous values do not exist, does it use the value of the property (the second part of TextBoxFor displaying initial value, not the value updated from code explains the behavior).
Except for the incorrect value attribute, the code you have shown for the <input> is the same as will be generated by simply using #Html.TextBoxFor(m => m.Question2). I assume your real case is different, but if you cannot make use of TextBoxFor() and using an overload that accepts htmlAttributes to generate the html you need, then the correct approach is to create your own HtmlHelper method (and making use of existing methods in the HtmlHelper class and System.Web.Mvc.Html namespace)

Reusing Custom Validation Across Views

I have an ASP.NET MVC 5 project. I have some custom validation needs. I know that in the end, a possible result for my HTML looks like this:
<div class="form-group has-error">
<label for="field">Field Label</label>
<div id="field">
<input name="field" class="form-control" type="text" autocomplete="off">
</div>
<div>
Field label is invalid. You need to do something else.
</div>
</div>
The validation errors are stored in a Dictionary<string, string> in the ViewBag. I was thinking of doing something similar to the following:
#string fieldErrorMessage = getFieldError('field', ViewBag.ValidationErrors)
<div class="form-group #if(fieldErrorMessage.length >0) { 'has-error' } ">
<label for="field">Field Label</label>
<div id="field">
<input name="field" class="form-control" type="text" autocomplete="off">
</div>
#if (fieldErrorMessage.length > 0) {
<div>
#fieldErrorMessage
</div>
}
</div>
My problem is, I do not know where to define getFieldError. I would like to use this function in multiple views. So I'm not sure a. where to define it. or b. how to call it. I'm not even sure if my approach to applying 'has-error' is correct. I feel like I have pseudocode more than mvc code.
Embedded functions in the page
For including functions in the page, you have two options.
Extending HtmlHelper:
You can extend HtmlHelper so that you can call Html.getFieldError("field"). Because ViewBag is in HtmlHelper, you won't need to pass that into the function. The best way to demonstrate this is by showing an example.
public static class ErrorValidation
{
public static MvcHtmlString getFieldError(this HtmlHelper<TModel> h, string f)
{
if(h.ViewBag.ValidationErrors.ContainsKey(f))
{
return MvcHtmlString.Create(h.ViewBag.ValidationErrors[f]);
}
return MvcHtmlString.Empty;
}
}
Adding a namespace in your views:
You can include a namespace in your views by adding a line to the Views\Web.config file. Then, you could use static methods like you planned. This is done by adding a line of something like <add namespace="MyProj.Validation" /> inside of <configuration><system.web.webPages.razor><pages><namespaces>. In addition, you can leave this out by calling the full reference to your function each time with MyProj.Validation.getFieldError(...).
Relying on MVC error handling
You can also use API's already built into MVC, which do allow for customized validation.
Doing error checks through model attributes:
The most simple way to do validation is by adding attributes to your model. If your model had a required field, you can simply add [Required] above the field in the class that defines your model. A plethora of validation attributes are provided by System.ComponentModel.DataAnnotations.
If you wanted to do a custom check, you could create your own attribute by implementing abstract class ValidationAttribute. Simply define the IsValid function with your custom logic.
If you have validation checks that need to happen on multiple fields in your model at the same time, you can have your model implement IValidatableObject. And to make this simpler, in the Validate function you don't need to create a list and add all your errors to that; simply return each ValidationResult object by putting the keyword yield at the beginning of the line. This would look like...
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
// Duplicate checks
List<String> fields = new List<String>();
for (var i=0; i<PhoneNumbers.Count; i++)
{
var item = PhoneNumbers[i];
if (PhoneNumbers.IndexOf(item) != PhoneNumbers.LastIndexOf(item))
{
fields.Add("PhoneNumbers["+i+"]");
}
}
if(fields.Count > 0)
{
yield return new ValidationResult("You cannot include duplicate phone numbers.", fields.ToArray());
}
// More validation checks
}
Doing error checks in the controller:
You can also do error checks in the controller, which allows for validation to vary depending on the action. This also lets you use the validation that already happened in the model with the ModelState object. In order to add errors to ModelState, simply call ModelState.AddModelError(). In order to check if a model is valid after all checks are done, you can check ModelState.IsValid.
Displaying errors in the view:
With validation happening in the ModelState object, you can display errors in your view by using the Html object. This allows you to generate a list of errors by calling Html.ValidationSummary() or display errors for individual properties with Html.ValidationMessageFor(...). Here's an extensive example...
for (var x = 0; x < Model.PhoneNumbers.Count(); x++ )
{
<tr>
<td>#Html.EditorFor(m => m.PhoneNumbers.ElementAt(x))</td>
#if(ViewData.ModelState["PhoneNumbers[" + x + "]"].Errors.Any())
{
<td>#Html.ValidationMessageFor(m => m.PhoneNumbers.ElementAt(x))</td>
}
else
{
<td>Ok!</td>
}
</tr>
}

Pass multiple values through ActionLink or Submit button in MVC

I have a scaffold list type table in my view. Above to the table, I have two checkboxes also. When I click on Edit link I need to pass the selected Id as well as the CheckBoxes' value also.
I can use ActionLink to pass the primary key value like
#Html.ActionLink("Action","Controller", new { id=#item.Id })
I can get the CheckBoxes Value by wrapping them into a Html.Beginform like,
#using(Html.BeginForm("Action","Controller",FormMethod="Post"){
<input type="checkbox" name="Check" value="1" />
<input type="checkbox" name="Check" value="2" />
-- table
<input type="Submit" value="Edit"/>
and in my controller, I can handle this like
[HttpPost]
Public ActionResult Edit(IEnumerable<string> check)
{ }
Here, I need to get both the Primary key value as well as Checkboxes' value, I tried in these two ways, and I could get any one of these only. Can anyone help me to get both the values? Thanks in advance.
You can include id in the action parameter. Like
[HttpPost]
Public ActionResult Edit(IEnumerable<string> check, int id)
{ }
Also, you will need to post the form using a submit button not the action link.
So, for sending the id, you will need to put it in a hidden field and it will automatically be sent on post.
Make sure you name the hidden field the same as the parameter name i.e. "id".
Edit
Do like this:
Take a hidden field in form: like this:
<input type="hidden" id="hf" value="TEst" name="hid" />
Then take a edit button in each row and make it call a javascript function. Like below:
<button type="button" onclick="clickfunc(#item.UniqueId)">Edit</button>
Next Come in Javascript and set hidden field and then do a form submit. Like below:
#section scripts{
<script type="text/javascript">
function clickfunc(id) {
$("#hf").val(id);
$('form').submit();
}
</script>
}
Now you get the values in your controller action. In my example it looks like:
public ActionResult EditTry(string hid)
{
return View();
}
You will get selected value in "check" string
To get the name type jquery on submit button click and store it in hidden field and access the hidden field from form collection
var getval = $("#Check option:selected").text();
$("#hdnText").val(getval);
This is controller Code:
[HttpPost]
Public ActionResult Edit(string check, FormCollection collection)
{
string strText = collection["hdnText"].ToString();
string strValue = check;
}

Handling hidden field in multiple forms in Mvc 4.0

I am having multiple forms in one page and that page inherits one single model.Every form submission requires a common value. So, common value is stored in hidden field. The hidden field is kept global i.e outside of all the forms but my problem is whenever I submit form, that hidden field is coming to be empty.The hidden field is #Html.HiddenfieldFor(m=>m.FkId) and this FkId is of string type proprty in model i.e public string FkId{get;set;} .Can somebody please guide me how to handle this situation. If I keep hidden field in one of the forms then , it is coming in controller but only for that form submission where I have kept it. But I want that property to be set once and can use in all the form submissions.Please help me. How can I sort out this problem
Some related code
<div id="tabs">
<ul>
<li>Nunc tincidunt</li>
<li>Proin dolor</li>
<li>Aenean lacinia</li>
</ul>
<div id="tabs-1">
#using (Ajax.BeginForm("Evaluation","SaveTab1"{new AjaxOptions { Onsucess= "DisplayMessage" }))
{
#Html.HiddenFieldfor(m=>m.fkID)
<input type="Submit" id="btnTab1" value="Submit" onclick="CheckUser();"/>
}
</div>
<div id="tabs-2">
#using (Ajax.BeginForm("Evaluation","SaveTab2"{new AjaxOptions { Onsucess= "DisplayMessage" }))
{
#Html.HiddenFieldfor(m=>m.fkID)
<input type="Submit" id="btnTab2" value="Submit" />
}
</div>
<div id="tabs-3">
#using (Ajax.BeginForm("Evaluation","SaveTab3"{new AjaxOptions { Onsucess= "DisplayMessage" }))
{
#Html.HiddenFieldfor(m=>m.fkID)
<input type="Submit" id="btnTab3" value="Submit" />
}
</div>
</div>
<script type="text/javascript">
function DisplayMessage(Json)
{
alert( $("#fkID").val(Json.hdn));
// and Alert is showing the value
$("#fkID").val(Json.hdn);
}
</script>
In the Controller I have:
public ActionResult SaveTab1(Model obj)
{
tbl ob =new tbl();
ob.FkId=Obj.fkID;
// after saving, I return
return json(new{hdn=Obj.fkID})
}
public ActionResult SaveTab2(Model obj)
{
tbl ob =new tbl();
ob.FkId=Obj.fkID;
//after saving, I return
return json(new{hdn=Obj.fkID})
}
Similar for tab three, but unfortunately the hidden filed only comes for first form submit. Then I return value to view by json and again set the hidden field property but then it comes null for second form.Please help
First of all, your View does not inherit the Model, it is strongly-typed to the type of your Model. These two are completely different.
But, to answer your question, there's no such a thing as a global hidden field. Fields are not variables. If you want a field to be posted to your Controller, you need to put it inside the form. If you have multiple forms in your View, then you'll have to put the same hidden field inside all the those forms. So, you need to put #Html.HiddenFor(m => m.FkId) inside all the forms in your View.
UPDATE: By the way, it's not Html.HiddenFieldFor, it's Html.HiddenFor.
I have solved the problem. Instead of taking same field in all the forms which was not working, I have taken a unique and different Hidden field properties and after on seperated success function of each form and set the every hidden field a value in success method.

ASP.NET/MVC3 - How do I perform validation on checkboxes and array?

I have this field in my model.
class AddUserModel
{
// ....other fields
[Required(ErrorMessage = "Please select at least one role.")]
public string[] Roles { get; set; }
}
In the view this is being rendered as a list of checkboxes:
<div class="editor-field">
#Html.ValidationMessageFor(model => model.Roles)
<ul class="list_roles">
#foreach (string role in ViewBag.PossibleRoles)
{
<li><input type="checkbox" name="Roles" value="#role" />#role</li>
}
</ul>
</div>
How do I get the validation error message to fire if none of the checkboxes are clicked? Will I need to write a custom validator?
I suspect you'll have to write a custom validator (which isn't hard, BTW; the biggest challenge would be to include client-side error checking, but even that's not that hard).
Another option might be to try using a listbox instead. In Html those have multiple selection capability. The listbox validator might let you require at least one value (I don't know for sure, as I haven't had reason to use a validated listbox in my MVC app yet).

Resources