I have
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
in my config and
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true);
in my view and
<script src="~/js/jquery.validate.min.js"></script>
<script src="~/js/jquery.validate.unobtrusive.min.js"></script>
are in the HTML source (as is jQuery) (no JS errors in the browser)
I have one form element with [Required] and the form element seems to be rendering OK with the associated data- attributes
<div class='form-group'>
<label class="col-md-2 control-label" for="Username">en-gb(Username)</label>
<div class='col-md-10'>
<div class='input-group'>
<div class='input-group-addon'><span class='fa fa-user'></span></div>
<input class="input-validation-error form-control" id="Username" name="Username" type="text" value="" />
</div>
<span class="field-validation-error help-block" data-valmsg-for="Username" data-valmsg-replace="true">en-gb(The en-gb(Username) field is required.)</span>
</div>
</div>
But no client side validation is occurring; the form is always submitted to the server.
What am I missing? What should I be checking?
Edit
TextBoxFor is in System.Web.Mvc.Html.InputHelpers and calls TextBoxHelper which in turn calls InputHelper in the same file. This calls
htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata) which looks like this:
public IDictionary<string, object> GetUnobtrusiveValidationAttributes(string name, ModelMetadata metadata)
{
Dictionary<string, object> results = new Dictionary<string, object>();
// The ordering of these 3 checks (and the early exits) is for performance reasons.
if (!ViewContext.UnobtrusiveJavaScriptEnabled)
{
return results;
}
FormContext formContext = ViewContext.GetFormContextForClientValidation();
if (formContext == null)
{
return results;
}
string fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (formContext.RenderedField(fullName))
{
return results;
}
formContext.RenderedField(fullName, true);
IEnumerable<ModelClientValidationRule> clientRules = ClientValidationRuleFactory(name, metadata);
UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules, results);
return results;
}
(Source: http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/5cb74eb3b2f3#src/System.Web.Mvc/HtmlHelper.cs)
The problem I have is that formContext.RenderedField(fullName) returns true and therefore no validation attributes are added to my input element.
In your views, are you rendering the following code?
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Assuming you're using the default project template, you'll need to include that within the view that you're rendering to enable client side validation... Alternatively, add Scripts.Render("~/bundles/jqueryval") to the bottom of your layout file to enable it across all your views.
Also do ensure that bundles/jqueryval is defined in the Bundles configuration file.
formContext.RenderedField(fullName) was a red herring, I really don't know what all that was about.
The problem is that I've subclassed the ValidationAttribute classes in order to provide globalisation with lookup from a database rather than from resx. This means that they do not appear in the ModelMetadata.
One solution is to implement the interface System.Web.Mvc.IClientValidatable on the overridden attributes. However, the base classes do not implement this interface, so they must be configuring client side validation by some other mechanism -- and it's this other mechanism that isn't smart enough to pick up subclasses of the out-of-the-box ValidationAttributes.
Related
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>
}
As for as I know it seems like Microsoft are using jQuery validation attributes as default for form input attributes.
Is it possible to configure my application so if I add the Required attribute and render my form using #Html.EditorFor(x => Model) the form will be rendered using required attributes instead of data-val-required? Or am I forced to write my own EditorTemplates for all standard types?
If you want to replace the standard data-* validation attributes used by ASP.NET MVC you should start by disabling unobtrusive client side validation in your web.config:
<add key="ClientValidationEnabled" value="false" />
This will prevent the html helpers from emitting them on your input fields.
Then you could write custom editor templates for the standard types. For example for string that would be ~/Views/Shared/editorTemplates/String.cshtml:
#{
var attributes = new Dictionary<string, object>();
attributes["class"] = "text-box single-line";
if (ViewData.ModelMetadata.IsRequired)
{
attributes["required"] = "required";
}
}
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, attributes)
And that's pretty much it. Now everytime you do an Html.EditorFor(x => x.Foo) where Foo is a string property it will generate the following markup:
<input class="text-box single-line" id="Foo" name="Foo" required="required" type="text" value="" />
It's also worth mentioning that if you don't want to disable unobtrusive client side validation and the data-* attributes for your entire application but only for a single form you could do that:
#using (Html.BeginForm())
{
this.ViewContext.ClientValidationEnabled = false;
#Html.EditorFor(x => x.Foo)
}
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.
I am looking for a solution which creates dynamic properties to be create in Model.
I want to use them in my View and Controller.
Can any one have idea, how to create it?
I am having scenario in my project where one page will be having options to be lets say Profile2, Profile5 etc.
Profile 2 can have two URLs to be submit from user.
Profile 5 can have five URLs to be submit from user.
and
So on........
Is there any solution or alternative to do this????
He Amit , For your situation "I am having scenario in my project where one page will be having options to be lets say Profile2, Profile5 etc.
Profile 2 can have two URLs to be submit from user.
Profile 5 can have five URLs to be submit from use"
You want to put this url in your properties ok.
SO do one thing create a property like this.
public List<string> urlList {get;set;}
use this in your property andd add url in the list.
you can add n no of urls.
That is exactly what ViewBag is for. Its a dynamic property on Controller and View.
public ActionResult SomeAction()
{
ViewBag.Message = "Hello, world";
}
<p>#ViewBag.Message</p>
This will allow you to send anonymous property values from your Controller to your View. However, if you're looking to post different numbers of values (urls in your example), you should use an IList as your model.
#model IList<string>
#for (int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(model => model[i])
}
Your model should probably store the values in a list. Here is an example explaining how to display and save data for a list property.
How to interact with List<t> in MVC
see this is what i have done as an alternative.
Make all divs and other fields in MODEL and use jQuery to work around.
I guess this is an alternative, but not exactly what i want. Still looking for answer. I post this as this can be helpful to some one in future.
Please check below.
<div>
#for (var i = 0; i < ProfileCount; i++)
{
<label>
URL:</label>
<input type="text" id=#string.Format("URL{0}", i) />
<label>
CheckName:</label>
<input type="text" id=#string.Format("URL{0}CheckName", i) />
<label>
Run Check From:</label>
#Html.DropDownList(string.Format("URL{0}Region", i), (IEnumerable<SelectListItem>)ViewBag.Regions)
<br />
<span id=#string.Format("URL{0}Result", i)></span>
<input type="button" value="Create check" id=#string.Format("URL{0}CheckSetup", i) onclick="getResponseFromUrl('#string.Format("URL{0}')", i);" />
<input type="button" value="Delete check" id=#string.Format("URL{0}Delete", i) onclick="DeleteCheck('#string.Format("URL{0}')", i);"
style="display: none" />
<input type="hidden" id=#string.Format("URL{0}Hidden", i) />
<br />
<br />
<br />
}
</div>
I have some problems with ASP.NET MVC’s default model binder. The View contains HTML like this:
<input name="SubDTO[0].Id" value="1" type="checkbox">
<input name="SubDTO[1].Id" value="2" type="checkbox">
This is my simplified ‘model’:
public class SubDTO
{
public virtual string Id { get; set; }
}
public class DTO
{
public List<SubDTO> SubDTOs { get; set; }
public DTO()
{
SubDTOs = new List< SubDTO>();
}
}
All this works fine if the user selects at least the first checkbox (SubDTO[0].Id). The controller ‘receives’ a nicely initialised/bound DTO. However, if the first check box is not selected but only, for example, SubDTO[1].Id the object SubDTOs is null. Can someone please explain this ‘strange’ behaviour and how to overcome it? Thanks.
Best wishes,
Christian
PS:
The controller looks like this:
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public RedirectToRouteResult Create(DTO DTO)
{
...
}
PPS:
My problem is that if I select checkbox SubDTO[0].Id, SubDTO[1].Id, SubDTO[2].Id SubDTOs is initialised. But if I just select checkbox SubDTO[1].Id, SubDTO[2].Id (NOT the first one!!!) SubDTOs remains null. I inspected the posted values (using firebug) and they are posted!!! This must be a bug in the default model binder or might be missing something.
This behavior is "by design" in html. If a check-box is checked its value is sent to the server, if it is not checked nothing is sent. That's why you get null in your action and you'll not find value in the posted form either. The way to workaround this is to add a hidden field with the same name and some value AFTER the check-box like this:
<input name="SubDTO[0].Id" value="true" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="true" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
In this way if you check the check-box both values will be sent but the model binder will take only the first. If the check-box is not checked only the hidden field value will be sent and you\ll get it in the action instead of null.
I think this post on Scott Hanselman's blog will explain why. The relevant line is:
The index must be zero-based and unbroken. In the above example, because there was no people[2], we stop after Abraham Lincoln and don’t continue to Thomas Jefferson.
So, in your case because the first element is not returned (as explained by others as the default behaviour for checkboxes) the entire collection is not being initialized.
Change the markup as follows:
<input name="SubDTOs" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTOs" value="<%= SubDTO[1].Id %>" type="checkbox">
What's being returned by your original markup is an unrelated set of parameters, i.e. like calling RedirectToRouteResult Create(SubDTO[0].id, SubDTO[1].id, ..., SubDTO[n].id) which is clearly not what you want, you want an array returned into your DTO object so by giving all the checkboxes the same name the return value to your function will be an array of ids.
EDIT
Try this:
<input name="SubDTO[0].Id" value="<%= SubDTO[0].Id %>" type="checkbox">
<input name="SubDTO[0].Id" value="false" type="hidden">
<input name="SubDTO[1].Id" value="<%= SubDTO[1].Id %>" type="checkbox">
<input name="SubDTO[1].Id" value="false" type="hidden">
You have to return something to make sure there is an element for each index, I suspect that any gap will cause a problem so I'd suggest using a 'null' ID, for example 0 or -1 and then process that out later in your code. Another answer would be a custom model binder.
There is always the alternate option of adding a property to your class that takes an array of strings and creates the SubDTO array from that.
public List<string> SubDTOIds
{
get { return SubDTO.Select(s=>s.Id).ToList(); }
set
{
SubDTOs = new List< SubDTO>();
foreach (string id in value)
{
SubDTOs.Add(new SubDTO { Id = id });
}
}
}
or something like that