input-validation-error on other input element? - asp.net-mvc

I have a model which has 2 fields, "a" and "b" like this:
class AbModel
{
public string a {get;set;}
[SomeValidation]
public int b {get;set;}
}
Now "b" is actually hidden in the gui, and "a" shows a textual representation of it.
<div class="editor-field">
<%= Html.HiddenFor(model => model.b, new { id = "ABModelEditor_b" })%>
<%= Html.TextBoxFor(model => model.a, new { id = "ABModelEditor_a" })%>
<input type="button" value="Change" onclick="AbModelEditorScript.showAbSelector(); return false;" />
</div>
Generates the following on "save" with empty data:
<div class="editor-field">
<input class="input-validation-error" id="ABModelEditor_b" name="b" type="hidden" value="">
<input id="ABModelEditor_a" name="a" type="text" value="">
<input type="button" value="Change" onclick="AbModelEditorScript.showAbSelector(); return false;" />
</div>
I need the the input-validation-error css class to be attached to the "a" textbox instead of the hidden for "b".
Before I simply move the css-class with a jquery-script, is there some other way that I should consider, perhaps some built in functionality?

Are you using unobtrusive validation? The other option is to add the validation attributes with jQuery and revalidate the form...
$("#ABModelEditor_a").attr("data-val-required", "true"); //add your validation attributes to the input tag
var form = $("form");
form.removeData("validator").removeData("unobtrusiveValidation") // this will clear the validation from the form
$.validator.unobtrusive.parse(form); //reattach the validation
This will give you real client side validation, but it still won't match your model. You'll have to fill in the gap on the server side.
In regards to your comment about server-side only validation.
You can try to manually add the model error to the model state. This should be reflected in your view when you return.
ModelState.AddModelError(string key, string errorMessage);

Related

Duplicated ID in ManageLogin.cshtml built-in template

I am using the built-in template which comes with ASP.NET MVC 4. The View which is causing problem is: ManageLogins.cshtml
This is how the output looks like:
So, this particular user has connected both Google and Facebook external IDs to his account:
And this is the Built in MS Template which produces the above code (I have changed the appearance a bit, but the code is the same)
#foreach (var account in Model.CurrentLogins)
{
<div class="form-row">
#using (Html.BeginForm("RemoveLogin", "Manage"))
{
#Html.AntiForgeryToken()
<div>
#Html.Hidden("loginProvider", account.LoginProvider)
#Html.Hidden("providerKey", account.ProviderKey)
<input type="submit" class="btn btn-default" value="Remove" title="Remove this #account.LoginProvider login from your account" />
</div>
}
</div>
}
The problem is that this code generates 2 inputs with id=loginProvider, and this gives me the browser error:
How can solve this problem?
Since model binding occurs on name and not id, I decided to remove the id from the hidden field and just use the name attribute:
#using (Html.BeginForm("RemoveLogin", "Manage", FormMethod.Post, new { #class = "inline-form float-right" }))
{
#Html.AntiForgeryToken()
<div>
<input type="hidden" name="LoginProvider" value="#Model.LoginProvider" class="d-none login-provider" />
<input type="hidden" name="ProviderKey" value="#Model" class="d-none provider-key" />
<input type="submit" class="btn btn-default" value="Remove" title="Remove this #account.LoginProvider login from your account" />
</div>
}
You can use an overload of the helper that allows you to provide htmlAttributes.
e.g.
for (int i = 0; i < 3; i++)
{
#Html.Hidden("foo", $"value{i}", new { id = $"foo{i}"})
...
}
Caveat: while the sample above shows how you can do whatever you want for name, value, id, etc., it may affect validation (and force you to customize that too). In your specific use case however, since it's just id, it should be fine.

Html.ValidationMessageFor not displaying at all

I have the following form:
<form action="~/buy-online" method="get" class="buy-online-form clearfix" autocomplete="off" id="buy-online-search">
<div class="infield-holder clearfix">
#Html.LabelFor(m => m.CustomerPostcode, new { #class = "infield" })
#Html.TextBoxFor(m => m.CustomerPostcode, new { #class = "textbox" })
<input type="submit" value="Buy Online" id="find-retailer-button" class="button" />
</div>
</form>
#Html.ValidationMessageFor(m => m.CustomerPostcode)
Which works fine and will display an error message when submitted without jQuery, but when I add the jQuery validate scripts (v 1.11.1):
<script src="/scripts/jquery.validate.js"></script>
<script src="/scripts/jquery.validate.unobtrusive.js"></script>
It stops the form submitting but doesn't display the error message
My property is marked like so:
[DisplayName("Enter your full postcode")]
[Required(ErrorMessage = "Please enter your full postcode")]
public string CustomerPostcode { get; set; }
And the html renders like this:
<input class="textbox" data-val="true" data-val-required="Please enter your full postcode" id="CustomerPostcode" name="CustomerPostcode" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="CustomerPostcode" data-valmsg-replace="true"></span>
If I inspect the input when I hit submit it is adding the class input-validation-error to the textbox but just not updating the validation message.
All the posts that I have checked on this problem just say to include the scripts so I'm at a loss as to why the message is not showing.
I've even tried adding the jquery.unobtrusive-ajax.js script but that didn't seem to do anything either. Any help would be appreciated, thanks.
You need to include the #Html.ValidationMessageFor(m => m.CustomerPostcode) within the form tags for jquery.validate.unobtrusive to work.

MVC5 Validation Errors in Razor View

I am using MVC5 and having problem displaying the validation errors of an object both in the Validation Summary section and the individual validation messages for each control.
Lets put aside client validation for the moment although we have ENABLED IT in the web config and included the js files.
If a model that is invalid is passed to a view that has in place a Validation Summary (setting excludePropertyErrors to false) and for each control there is a #Html.ValidationMessageFor(model => model.Summary, "", new { #class = "text-danger" }), shoudn't all these display the Validation errors in the object when the page loads.
We checked the object and the framwork knows it is invlaid (ModelState.IsValid is definitely false) and the errors collection contains several validation errors
(System.Data.Entity.Validation.DbValidationError) but none of them show up neither in the ValidationSummary or the ValidationMessage for the individual controls when the page loads.
Currently I am getting he validation errors manually by context.Entry(prop).GetValidationResult().ValidationErrors.ToList(); and passing it to the Viewbag and iterate over them in the view to display them. Seems crazy!
P.S. The main View is composed of several partial views in which the controls are and the Vaidationsummary is in the Main containing view.
public async Task<ActionResult> GetProduct(id)
{
var product= await context.Products.FindAsync(id);
return View(product);
}
As I mentioned , there are NO VALIDATION MESSAGES ANYWHERE. If we turn off client side validation the input control html is
<input class="form-control text-box single-line valid" id="Price" name="Price" style="font-weight:bold" type="text" value="" aria-invalid="false">
As you can see the value is empty BUT it says it is valid even though the field is a required field marked so via DataAnnotations and the Model is INVALID and includes validation error that Price field is required!
Here is the code:
<div class="form-group">
#Html.LabelFor(m => m.Price, new { #class = "col-sm-3 control-label" })
<div class="col-sm-5">
#Html.EditorFor(model => model.Price, new { htmlAttributes = new { #class = "form-control", style = "font-weight:bold" } })
#Html.ValidationMessageFor(model => model.Price, "", new { #class = "text-danger" })
</div>
</div>
And this is the generated html with client side validation enabled :
<input class="form-control text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" style="font-weight:bold" type="text" value="">
Would greatly appreciate any assistance.

I am attempting to return a model from a form that shows the details of said model but am returning a null value

My form is not returning a null model.
I am viewing the details of a specific model on a Details page and then, if a user presses the SubmitPost button, the view should return the model.
View
#model MyApp.Models.MyModel
#using (Html.BeginForm("ApplyUsingDetails", "Controller", FormMethod.Post))
{
<fieldset>
<legend>Course</legend>
<div class="display-label">MyModel ID</div>
<div class="display-field">
#Html.DisplayFor(model => model.CourseID)
</div>
<div class="display-label">Title</div>
<div class="display-field">
#Html.DisplayFor(model => model.Title)
</div>
etc. . .
<p><input type="submit" value="SubmitPost" /></p>
<more misc information for the view>
</fieldset>
}
Action
[HttpPost]
public ActionResult ApplyUsingDetails(MyModel model)
{
// when I try to access model here it comes up null
}
To give some context: I am using this form to allow a student to view the details of a class and then apply to the class at the click of a button.
Any clue as to why my model would be null?
Based on the sample you provided, it doesn't look like you are including any of MyModel's fields in the form. As a test, try including a hidden field or use #Html.HiddenFor(model => model.CourseID) and see if you have any values posted.
DisplayFor HTML helper will simply return a string that represents the Property value usually. If your Property is a boolean type, it wll render it as a disabled checkbox. As long as you dont have any input controls you wont be getting the value. So in this case, you can simply use the #Html.HiddenFor HTML helper method.
HTML.HiddenFor Helper Method generates HTM Markup for an input element of type hidden with the id and name same as the Expression
#using (Html.BeginForm("ApplyUsingDetails", "Controller", FormMethod.Post))
{
<div class="display-label">MyModel ID</div>
<div class="display-field">
#Html.DisplayFor(model => model.CourseID)
#Html.HiddenFor(m=>m.CourseId)
</div>
<input type="submit" value="Save" />
}
Now the Value of Course id will be available in your HTTPPost action method.
You need to include input elements in your form so that they can be posted to your controller and bound to your controller's model. Model Binding in ASP.Net MVC takes the form values that you submitted and looks for a property on your controller's model with the same name and tries to set the propery with the form value. No input fields in your form means nothing for Model Binding to stick into the controller's model.
I would suggest using readonly input elements styled with css to look like div.display-field.
<div class="display-label">Course ID</div>
<div>
#Html.TextBoxFor(model=>model.CourseID, new{readonly="readonly", #class="display-field"})
</div>
Wrapping the input in a div may or may not be needed depending on other styles on the page.

Do MVC3 non-sequential hidden input indexes need to come first?

MVC3 non-sequential index hidden inputs for model binding..
<input type="hidden" name="Index" value="whatever" />
Does it matter if they go before, after, in the middle of the other related inputs to be posted?
Does it matter at all where they end up in the posted data?
For example, can they all be lumped together and it still works?
<input type="text" name="[A].Id" value="1" />
<input type="text" name="[B].Id" value="2" />
<input type="hidden" name="Index" value="A" />
<input type="hidden" name="Index" value="B" />
No, the order of your form fields does not matter, nore where they appear on the html page.
The most important factor for MVC3 is the name of the fields must match to the name of your controller/action parameter.
If you have two fields with the same name however, only one value will be returned into your action.
As long as the hidden fields are located inside of the form it should not matter the order in which they are placed. Please see code sample below. Notice how the hidden fields are put anywhere inside of the form.
#using (Html.BeginForm())
{
#Html.ValidationSummary(false, "Please correct the following errors")
#Html.HiddenFor(m => m.CoolStuffId)
#Html.Partial("_EditCoolStuff", Model)
<fieldset class="ui-grid-a">
<div class="ui-block-a"><a data-role="button" href="#Url.Action("ActionPlan", "Store", new { id = Model.StoreID })">Cancel</a></div>
<div class="ui-block-b"><button type="submit" data-theme="a">Submit</button></div>
</fieldset>
#Html.HiddenFor(m => m.TypeId)
}

Resources