Remote validation with additionalfields >> one more POST? - asp.net-mvc

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.

Related

Checking to see if Html Input textfield has value

On my asp.net web app (mvc), I am wanting to check if the user entered a value in the text field when the button is clicked, and if it is to redirect to the next page. If not, to throw an exception. I looked up on how to do so, and attempted to use it with Html.BeginForm() but for some reason it still does enter my condition statement when I leave the textbox blank and hit the submit button.
ORIGINAL CODE:
SecondController:
[HttpPost]
public IActionResult Insert(String inputF)
{
if (inputF == null)
{
throw new Exception("You did not type anything in the textfield!!!!");
}
else
{
return RedirectToAction("FinalIndex", "Final");
}
}
I wanted it to throw an exception but not stop the program for proceeding like it return a message to the user in the UI but still record the exception that was made.
UPDATED CODE:
My SecondController:
[HttpPost]
public IActionResult Insert(String inputF)
{
if (!ModelState.IsValid || String.IsNullOrWhiteSpace(inputF.Input))
{
return View(inputF);
}
else
{
return RedirectToAction("FinalIndex", "Final");
}
}
It does not let me do String.IsNullOrWhiteSpace(inputF.Input) because it shows the following:
So I then removed the .Input from inputF.Input and it still gave me the the unhandled message in the picture below
My View:
#using CustomerSimulatorApp.Models
#model TextInput
<h2>Second page</h2>
#using (Html.BeginForm("Insert", "Second", FormMethod.Post))
{
#Html.TextBoxFor(r => r.Input);
<input id="Button" type="submit" value="button" />
}
My model:
namespace CustomerSimulatorApp.Models
{
public class TextInput
{
[Required, MinLength(1)]
public String Input { get; set; }
}
}
When I leave the text box empty & hit the button it returns this shown below:
Not sure, if that is what is exactly supposed to happen or if an actual exception was supposed to be thrown. Also, when I just type a single character in the box and hit the button it still allows me to proceed to the redirect and does not throw an exception regardless of my [Required MinLength(2)] on my attribute.
So my objective is to redirect the user to another page (FinalIndex) if the user typed something into the textbox and hits the button. If the user did not type anything in the text box and still hits the button to proceed to the next page, then it needs to throw an exception.
Am I doing something incorrectly? Any suggestions would be greatly appreciated it!
We got this resolved over chat, just had a few naming conventions we changed:
public IActionResult SecIndex(TextInput form)
Was the ultimate fix, instead returning a non-existing view.
You are almost there, but few changes are needed in view and controller side to get it working.
you need to have input button with type submit
#using (Html.BeginForm("Insert", "Second", FormMethod.Post))
{
#Html.TextBoxFor(r => r.Input);
<input id="Button" type="submit" value="button" />
}
and in controller action code should be like:
// this if checks if input textbox value is not present load back page with error
if (!ModelState.IsValid || String.IsNullOrWhiteSpace(inputF.Input))
{
return View(inputF);
}
else
{
// there was value in input redirect to Final controller action
return RedirectToAction("FinalIndex","Final");
}

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.

FluentValidation message appears, but form submits anyway

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;

How to return a single string to a div in my current page from HTTP POST in ASP.NET MVC 5?

I'm trying to post a message after a contact form, indicating to the user that their message has been sent after they click the submit button. I don't want to redirect to a different page or to return a different view inside my HTTP Post action method. How do I do something like that in ASP.NET MVC framework?
Below is my code sample:
#*contactus.cshtml*#
#model MySite.Models.ContactModel
#using (Html.BeginForm())
{
<div class="col-md-6">
<div class="form-group">
#Html.TextBoxFor(model => model.Name})
<p>#Html.ValidationMessageFor(model => model.Name)</p>
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Email)
<p>#Html.ValidationMessageFor(model => model.Email)</p>
</div>
<div class="form-group">
#Html.TextAreaFor(model => model.Message)
<p>#Html.ValidationMessageFor(model => model.Message)</p>
</div>
<div class="col-lg-12">
<button type="submit">Send Message</button>
</div>
</div>
}
#*ContactModel.cs*#
public class ContactModel
{
[Required(ErrorMessage = "* Please enter your name.")]
[StringLength(100, MinimumLength=3, ErrorMessage="* Please enter your full name.")]
public string Name { get; set; }
[Required]
[EmailAddress(ErrorMessage="* Not a valid email address.")]
public string Email { get; set; }
[Required]
public string Message { get; set; }
}
I only have a contact us form right now on my home/index page, and I don't want to redirect it to any other pages. I would like to display a message right below the Send Message button, but I'm not sure how to go about it using the action method below:
#*HomeController.cs*#
public ActionResult Index(ContactModel model)
{
if (ModelState.IsValid)
{
// this is my helper library, for brevity, I'm not copying it.
EmailHelper emailService = new EmailHelper();
bool success = emailService.SendEmail(model.Name, model.Email, model.Message);
return Content(success ? "success" : "no...something went wrong :(");
} else {
return View(model);
}
}
Right now this controller will return the string inside Content which replaces my entire page, and I would like the string to be returned below my contact form. Also, I have two sections on the same html page with Contact Form as the second one, when I return View(model), it automatically redirects to the first section, which isn't ideal... How do I tell the controller to only redirect it to the second section after the POST method? In addition, I feel like it would be more efficient if it didn't return the whole page... so is there a way to only return a Message string to the div?
You can place a hidden div on the page which will contain the message.
Then when your form has been submitted, capture the click event for your button, and use that to display the hidden message.
Let me know if you need a code example. Posting your form would help us answer you more specifically.
To only show the success message if the form is successfully sent, I would recommend setting a value in the ViewBag in the POST action of the controller and then returning that same page if you want to still have the same page showing. On the View itself, you could then place an If statement to test if the ViewBag variable contains a value and if so, display the message.
Controller:
[HttpPost]
public ActionResult YourAction(YourModel m)
{
//Do stuff to send the contact form
...
if(error)
{
ViewBag.Message = "There was a problem sending the form.";
}
else
{
ViewBag.Message = "The form was sent successfully!";
}
return View(m);
}
View:
#if(ViewBag.Message != null)
{
<div>#ViewBag.Message</div>
}
This lets you check if the form was posted successfully on the server before telling the user the result and will only display a message if ViewBag.Message has been set. Note that you can have as many ViewBag variables as you want and can name them whatever you want... just remember which one you use in which place.
EDIT:
Following the comments, this could also be done using an AJAX call. I'll use the jQuery .post() method for simplicity sake.
In Script:
<script>
$(document).on('click', "#buttonId", function() {
var nameText = $("#IdOfNameField").val();
var emailText = $("#IdOfEmailField").val();
var messageText = $("#IdOfMessageField").val();
$.post('#Url.Content("~/Controller/AJAXPostContactForm")',//the url to post to
{name: nameText, email: emailText, message: messageText }, //these are values to be sent to the action
function(){ //this is the success function
$("#successMessage").val("Form posted successfully.");
}
)
.fail(function() {//failure function
alert("Something went wrong.");
});
}
</script>
Controller:
public void AJAXPostContactForm(string name, string email, string message)
{
try
{
//do stuff with the information passed into the action
}
catch(exception e)
{
//Handle errors. If error thrown, Ajax should hit fail block in script
}
finally
{
//do any cleanup actions
}
}
View:
<div id="successMessage"></div>
I have not tested this code but it should theoretically work. On a specific button click, it will get the values from the form fields, post those values to a specialized ActionResult in the controller, and then return a message about what happened.

Resources