FluentValidation message appears, but form submits anyway - asp.net-mvc

Using MVC4 with FluentValidation. I have a field with two rules on it. The NotEmpty rule works as expected. The Matches rule seems to fire, but the form submits anyway, even though the validation message pops up as if it's failing validation.
I have the following view model and validator:
public class ImpactedEntityViewModelValidator : AbstractValidator<ImpactedEntityViewModel>
{
public ImpactedEntityViewModelValidator()
{
RuleFor(x => x.ImpactedEntityDescription)
.Matches("[a-zA-Z0-9/ ]{1,}").WithMessage("Description can only contain letters, numbers, '/', and spaces.")
.NotEmpty().WithMessage("Description is required.");
}
}
[Validator(typeof(ImpactedEntityViewModelValidator))]
public class ImpactedEntityViewModel
{
public int? ImpactedEntityLUID { get; set; }
[Display(Name = "Impacted Entity Description")]
public string ImpactedEntityDescription { get; set; }
public bool? Deleted { get; set; }
}
View:
#model ChangeControlForm.Models.ImpactedEntityViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.EditorFor(model => model)
<p>
<input type="submit" value="Create" class="btn btn-default" />
</p>
}
Added to Global Application_Start:
FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure();
I'm not sure how that's possible. It won't submit if the field is left empty, as expected. If I enter a "%" for example, the message for the Matches rule will pop up but then it will immediately submit after and write the record. Is there something I'm missing that could cause that?
Thank you.
Per Michael Crook's answer:
This solved the issue:
$("form").submit(function () {
var form = $(this);
if (form.valid()) {
// do valid stuff
}
else {
return false;
}
});
Per LeftyX's answer:
Checked my Nuget packages and jQuery had an update available. Updating it to 2.1.4 fixed the issue and I don't need the extra check on submit.
Thanks everyone.

You don't really have to do the check for validation yourself:
$("form").submit(function () {
var form = $(this);
if (form.valid()) {
// do valid stuff
}
else {
return false;
}
});
I mean, you can, but probably you probably already have everything you need in place.
If you check in your Scripts folder you should have:
jquery.validate.js
jquery.validate.unobtrusive.js
and
jquery.unobtrusive-ajax.js (this is only needed if you're POSTing ajax)
and you BundleConfig already bundles the scripts needed for the client-side validation:
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
the default template doesn't add the reference to the bundle automatically but you can add it simply adding:
#Scripts.Render("~/bundles/jqueryval")
to your _Layout.cshtml or wherever you need the client-side validation.
if you check the html for your form you will see that your input:
<input name="ImpactedEntityDescription" class="text-box single-line" id="ImpactedEntityDescription" type="text" value="" data-val="true" data-val-required="Description is required." data-val-regex-pattern="[a-zA-Z0-9/ ]{1,}" data-val-regex="Description can only contain letters, numbers, '/', and spaces.">
have all the unobtrusive attributes set in place:
data-val="true"
data-val-required="Description is required."
data-val-regex-pattern="[a-zA-Z0-9/ ]{1,}"
data-val-regex="Description can only contain letters, numbers, '/', and spaces."
jquery.validate.js checks the form before sumitting for you (line 404):
// http://jqueryvalidation.org/Validator.form/
form: function() {
this.checkForm();
$.extend( this.submitted, this.errorMap );
this.invalid = $.extend({}, this.errorMap );
if ( !this.valid() ) {
$( this.currentForm ).triggerHandler( "invalid-form", [ this ]);
}
this.showErrors();
return this.valid();
},
Check your nuget packages are updated.

You will probably find that FluentValidation (I've only ever used for server side validation, not client side) doesn't have the ability to disable posting. You could try using jquery to search form for validation error classes and then disable the button yourself.

Aside from the possible causes mentioned already above, this line in your view can also cause a submit of the form as soon as a user hits your submit button, even though the form is still invalid:
HtmlHelper.ClientValidationEnabled = false;

Related

ASP Net Core - hidden input field data is not passed in the Controller

I need to pass which button is clicked for the form submit. But the hidden input is not received in the controller. Below are my code snippets
View Model:
public class DocumentViewModel
{
public int Id { get; set; }
public int ActionId { get; set; }
}
razor(cshtml):
<form....>
<input id="docActionId" name="docActionId" asp-for="ActionId" type="hidden" value="initialValue" />
</form>
JavaScript:
$("#save_btn").on("click", function ()
{
$("#docActionId").val("test1");
});
$("#submit_btn").on("click", function ()
{
$("#docActionId").val("test2");
});
I tried showing the current value in alert function if it is change and it did.
In my Controller/Action when I debug, I get a null value for the ActionId variable. I think this is very simple but I don't know what I did wrong.
Please help. I'm stucked in this for 2 hours now. Thanks in advance.
remove the "Id" attribute... that's all you need. "asp-for" does that. adding yours duplicates it kind
Remove the name="docActionId" html attributes, they will be generated automatically by Razor.
Specifically by the asp-for="ActionId" attribute.
Furthermore the reason this doesn't work is because the name attribute which is docActionId != ActionId which is the name of the property of your object. So it doesn't know where to bind it to.
<form id="myform">
<input id="docActionId" asp-for="ActionId" type="hidden" value="initialValue" />
<button type="submit" id="save">Submit</button>
</form>
Try this javascript perhaps the form is submitted before you attach the value
var saveButton = documnet.getElementById('save')
saveButton.addEventListener('click', function(e) {
e.stopPropagation();
documnet.getElementById('docActionId').value = "test value"
documnet.getElementById('myform').submit()
})
<input type="hidden" id="tag" >
type="hidden" didn't work for me with .net core 7 but only hidden keyword worked.
<input hidden id="tag">

Can't bind checkbox in partial view to the main model (MVC)

I've been running into to issue and I've been searching for an answer but nothing helped.
I have a Model:
public class Filters
{
public bool Filter1 { get; set; }
public bool Filter2 { get; set; }
public bool Filter3 { get; set; }
etc...
}
I have a partial view with multiple checkboxes and tried multiple things:
<input id="Filter1" name="Filter1" type="checkbox" value="true">
<input type="hidden" value="false" name="Filter1" />
and
#Html.CheckBoxFor(model => model.Filter1)
Then I have a main model:
public class Dashboard
{
...
public Filters FiltersDashboard { get; set; }
}
And somewhere in the main view I insert the partial view like this:
#Html.EditorFor(model => model.FiltersDashboard, "Filters")
In a jquery, I execute an alert when the checkbox is clicked and shows the value of the checkbox. This value remains unchanged.
<script>
$("#Filter1").click(function () {
alert(" #Model.FiltersDashboard.Filter1 ")
});
</script>
EDIT: Also tried it in the .submit function but model value remains unchanged:
<script>
$("#searchform").submit(function (event) {
alert(" #Model.FiltersDashboard.Filter1 ")
event.preventDefault();
});
</script>
This tells me that something isn't correctly bound but I have no clue what I'm doing wrong.
Also, the reason I'm not using a checkboxlist is because I need to execute a different query for each filter so I need specific names and bindings for them.
#Model.FiltersDashboard.Filter1 is razor code and is executed on the server before its sent to the view, so it will only ever return the initial value of the property (if you inspect the source code, you will see the initial value has been hard coded in your script).
However, if your script is being executed, then it means that you are using the manual <input> tags in your 2nd code snippet, in which case your view is not binding to your model because the correct name attribute would be name="FiltersDashboard.Filter1" and the associated id attribute would be id="FiltersDashboard_Filter1".
Always use the strong typed #Html.CheckBoxFor() which will generate the correct html for 2-way model binding and client side validation.
Note also that it just needs to be #Html.EditorFor(model => model.FiltersDashboard) - the 2nd parameter is unnecessary.
Then the script should be
<script>
$('#FiltersDashboard_Filter1').click(function () {
var isChecked = $(this).is(':checked');
alert(isChecked);
});
</script>

ValidationMessageFor not showing up in boostrap modal

Inside my bootstrap modal is a form. When I click the submit button , the validation message error is not showing up. (I intend to enter/input invalid data). I reload the boostrap modal if there is detected error , and I do this in the controller in order to keep the modal open. Is there a different approach here? I wonder why the error message doesn't show up.
Model:
[Display(Name="Email:")]
[EmailAddress(ErrorMessage="Invalid Email Address!")]
public string Email { get; set; }
View:
#Html.LabelFor(e => e.Email, new { #class = "control-label col-md-2" })
<div class="col-md-2">
#Html.EditorFor(e => e.Email, new {htmlAttributes = new { #class = "form-control", required = "required" } })
#Html.ValidationMessageFor(model => model.Email)
</div>
Controller:
//for submit button
if (ModelState.IsValid)
{
//Proceed to Saving
}
//Keep modal open to show validation message
TempData["HasError"] = "YES";
return RedirectToAction("Index");
//Index action
[HttpGet]
public ActionResult Index()
{
//send this in frontend to know if bootstrap modal will reload
ViewBag.Status = TempData["HasError"];
return View();
}
Frontend / js
<script>
$(function (){
var x = '#status';
//show the modal of registration form since there is a validation message error
if (x == "YES") {
var tab = 'SignupFrm';
$('#' + tab).addClass("tab-pane fade in active");
$('.nav-tabs a[href="#' + tab + '"]').tab('show');
$('#mLogin').modal('show');
}
})
</script>
From your comments, you have not included the scripts for client side validation, so first step is to include them (after your jquery 2.1.4 min.js script)
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
or better (assuming your have the default bundles in your BundleConfig.cs file)
#Scripts.Render("~/bundles/jqueryval")
Assuming you have not turned off client side validation, then this will now display the validation error message(s) when you click the form's submit button and cancel the submit (your controller POST method will not be hit).
However, you still need to handle it in the server in case a malicious user by-passes it. Currently, if your model is not valid, you do a redirect to the GET method, which means that ModelState is lost. Instead you need to return the view, and to ensure the modal is reopened, you can add a ViewBag property or just check for ModelState errors in the view). Your POST method should be (note I am assuming the model is Person.cs)
[HttpPost]
public ActionResult Index(Person model) // modify class name to suit
{
if (!ModelState.IsValid)
{
ViewBag.HasErrors = true;
return View(model);
}
// save and redirect
}
Then in your script
$(function () {
if ('#ViewBag.HasErrors') {
// display the modal
}
)};
An alternative to adding a ViewBag property would be to use the following
$(function () {
if ('#ViewContext.ViewData.ModelState.Values.SelectMany(v => v.Errors).Any()') {
// display the modal
}
)};
Side note: You have added required = "required" to your textbox suggesting you want the Email to be required. Adding this attribute does not give you server side validation and adding the jquery validation scripts means it will be ignored (the novalidate="novalidate" attribute is added to your <form> element). Remove it and instead add the RequiredAttribute to your property so that you get both client and server side validation.
[Required(ErrorMessage="Please enter an email address")]
[EmailAddress(ErrorMessage="Invalid Email Address!")]
public string Email { get; set; }
You might want to check if the generated input's "id" and (I think) "name" attributes match the validation's <span> "data-valmsg-for" attribute.
I had a case where I needed custom values for these had the same issue for validation message not showing on client and that was my solution:
#Html.ValidationMessage("newMarkValue", new { #class = "text-danger" })
#Html.EditorFor(modelItem => item.NewMark, null, "newMarkValue", new { htmlAttributes = new { #class = "form-control" } })
Renders the validation <span> as:
<span class="field-validation-valid text-danger" data-valmsg-for="newMarkValue" data-valmsg-replace="true"></span>
And the input, according to the property's Range attribute [Range(2,6, ErrorMessage="Range 2 - 6."] as:
<input class="form-control text-box single-line" data-val="true" data-val-number="The field NewMark must be a number." data-val-range="Range 2 - 6." data-val-range-max="6" data-val-range-min="2" data-val-required="The NewMark field is required." id="newMarkValue" name="newMarkValue" type="number" value="0">
I tried everything from above but nothing seems to work for me.
I tried this , just wrote the script part in my partial view and everything worked.

Remote validation with additionalfields >> one more POST?

I have an ASP.NET MVC3 solution. I use remote validation for checking whether a username already exists.
At a first stage, I user the following validation attribute:
[Required, Remote("UserNameAlreadyExists", "User", Error="Already exists!"]
public string UserName { get; set; }
Here is the trace at runtime:
Then I realised I need to pass additional field (userID) to validate thus I do:
[Required, Remote("UserNameAlreadyExists", "User", AdditionalFields="UserID", Error="Already exists!"]
public string UserName { get; set; }
Here is the trace at runtime:
You'll see that there is one more POST ?!
Why? Tt is a problem in my situation. This drives me crazy.
Thanks for your help.
UPDATE
Here is the code of my Edit.cshtml
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.UserID)
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
...
<input type="submit" value="Submit">
}
Here is the javascript code
$('form', dialog).submit(function () {
// Do not submit if the form does
// not pass client side validation
if (!$(this).valid()) {
writeError("Validation failed!");
return false;
}
// On the line below: I retrieve the POST url + serialize all submitted data from my form
$.post($(this).attr('action'), $(this).serialize(), function (data, status) {
...
...
}).error(function (error, status, a, b) {
alert('Error!');
});
// Unbind form submitting
$('form', dialog).unbind();
return false;
});
When I debug my code (step by step) I can note some points:
the form is submitted and intercepted by my jquery function
the UserAlreadyExists is triggered (why?). In this function (on the second line) I have: var user = _requestServiceClient.GetUserFromUserName(userName); WHEN step on this line, I suddently step into post action of Edit. Strange!? Some step later, it step back in the function UserAlreadyExists. Still strange!?
I don't know if the problem is in my code or if my version of jquery.validate.min.js is outdated and bugged or something else...
I noticed that if I don't use any AdditionalFields, I don't have any problems!!
I also noticed that if I trigger the UserAlreadyExists volontary (by focus in the field) I don't have any problems when submitting (posting)!!
So I have problems when the form Edit is showed and UserName field is not focused! Then I submit and problems arrived
Thanks.

unobtrusive client validation in knockout template binding

I have a model with data annotations and i am an dynamically binding that with viewmodel using knockout template binding and mapping plugin. I am trying to do a unobtrusive client validation to be done on my model. How we can do that in this scenario. Any help/suggestions?
public class MyUser
{
[Required]
[StringLength(35)]
public string Username { get; set; }
[Required]
[StringLength(35)]
public string Forename { get; set; }
[Required]
[StringLength(35)]
public string Surname { get; set; }
}
In my view i am dynamically template binding a list of MyUser using ajax.
public JsonResult TestKnockout()
{
IList<MyUser> myUserList = new List<MyUser>();
myUserList.Add(new MyUser { Username = "ajohn", Surname = "surname" });
myUserList.Add(new MyUser { Username = "ajohn1", Surname = "surname1" });
return Json(myUserList, JsonRequestBehavior.AllowGet);
}
}
View:
<form id="Userform" action='#Url.Action("Save", "Home")' data-bind="template: {name: 'UserTemplate', foreach:UserList}">
<input type="Submit" name="name" value="Submit" />
</form>
<script id="UserTemplate" type="text/Html">
<input type="text" data-bind="value: Username"></input>
<input type="text" data-bind="value: Forename"></input>
<input type="text" data-bind="value: Surname"></input>
</script>
<script type="text/javascript">
var viewModel = {
UserList: ko.observableArray(new Array()),
Save: function () {
//// reached here means validation is done.
alert("Save");
}
}
ko.applyBindings(viewModel);
$.ajax({
type: 'GET',
url: '../Home/TestKnockout',
contentType: "application/json",
success: function (data) {
$.each(ko.mapping.fromJS(data)(), function () {
viewModel.UserList.push(this);
})
// attach the jquery unobtrusive validator
$.validator.unobtrusive.parse("#Userform");
// bind the submit handler to unobtrusive validation.
$("#Userform").data("validator").settings.submitHandler = viewModel.Save;
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
</script>
pilavdzice and drogon answers are quite good but we forget the basic point.
Since we are using an MVVM pattern for the seperation of UI and data (+vm) we don't want to perform UI validation but DATA VALIDATION. Those two are quite different, jquery validate is a great plugin but it does UI validation (it starts from UI to check the fields).
I have found knockout validation plugin which seems to do quite well and what it does is to go the opposite road, it validates your viewmodel and not your UI (it actually maps to UI elements to display the errors).
Unfortunately If your viewmodel gets complex, that plugin will have some problems, but in any case this is the way to go.
UI validation is perfect as long as we are not using an MVVM pattern, after all what do we separate the components (M-V-VM) for ?
Hope I helped !
Thanks!
I had the same problem as you so I wrote the following component.
https://www.nuget.org/packages/ScriptAnnotations/
https://scriptannotations.codeplex.com/
Please let me know if this helps.
I would go with jquery's event binding for this.
First, add your data-val attributes to the inputs you want to validate. (To figure out which data-val attributes to use, I usually bind a form server-side to a model and view source.)
<input data-val-required="test" data-val="true" data-bind="visible:
$parent.userEditMode, value: FirstName" />
Second, add a validation utility function --this calls the jquery validation plugin used by MVC under the covers.
function validateForm(thisForm) {
var val = thisForm.validate();
var isValid = val.form();
alert(isValid);
if (!isValid) {
thisForm.find('.input-validation-error').first().focus();
}
return isValid;
}
Third, call validate before issuing your viewmodel method. Make sure to remove the "click" data-bind attribute from the markup in your page.
$('#..your form id...').live('submit', function (e) {
e.preventDefault();
if(validateForm($(this)))
viewModel.saveUser();
});
If you are using knockoutjs and jquery, I came up with the following very simple method for doing basic validation.
Wherever you want to display the error message on your page, include a span tag like this:
<span name="validationError" style="color:Red"
data-bind="visible: yourValidationFunction(FieldNameToValidate())">
* Required.
</span>
Obviously you need to write "yourValidationFunction" to do whatever you want it to do. It just needs to return true or false, true means display the error.
You can use jquery to prevent a user from proceeding if any validations errors are displayed. You probably already have a save button that triggers a javascript function to do some ajax or whatever, so just include this at the top of it:
if ($("[name='validationError']:visible").length > 0) {
alert('Please correct all errors before continuing.');
return;
}
This is a lot simpler and more flexible than many other validation solutions out there. You can position your error message wherever you want, and you don't need to learn how to use some validation library.

Resources