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.
Related
I have a #Html.EditorFor that represents a search field. I am trying to do a search in the controller when the text in the field is changed.
I can't figure out how to go to the postback every time the text is changed in the input, and not when the submit button is clicked.
Model:
public class MainWindow
{
[Key]
public int MainWindowId { get; set; }
public string SearchedString { get; set; }
}
View:
#using (Html.BeginForm())
{
<div>
<label>search:</label>
#Html.EditorFor(model => model.SearchedString, new htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SearchedString, "", new { #class = "text-danger" })
<input type="submit" value="search" class="btn btn-default" />
</div>
}
Controller:
[HttpPost]
public ActionResult Index([Bind(Include = "MainWindowId,SearchedString")] MainWindow mw)
{
ManageProduct mp = new ManageProduct();
if (ModelState.IsValid)
{
//search code
return View("Index",mw);
}
return View(mw);
}
Use AJAX to submit the form. I will use jQuery in my example.
Listen to changes of the <input> rendered by the Editor.
EDIT: To achieve this, we use the ID that the editor gave to the HTML input as jQuery selector (do not change the ID, because the MVC modelbinder expects it to be in a certain format). Find the ID using the browser Developer Tools (F12).
You will also need to give an ID to the form element so we can serialize it to get the post data. Also provide a placeholder into which to render the results.
#using (Html.BeginForm("Index", "YourController", FormMethod.Post, new { id = "formId" })) {
<div id="placeholderId"></div>
#Html.EditorFor(model => model.SearchedString, new htmlAttributes = new { #class = "form-control" } })
JS function to post the form, embedded in the razor view:
<script type="text/javascript">
function postForm() {
$.ajax({
url: $('#formId').attr('action'),
type: 'POST',
data: $('#formId').serialize(),
success: function(resultData) {
if (resultData) {
// update some placeholder element with the received data
$('#placeholderId').html(resultData);
}
}
});
}
JS to listen to changes of the input, embedded in the razor view. Listen to the keyup event, because change will only fire after the input loses focus. Here I assume that the editor gave id="SearchedString" to the input (may vary, e.g. if this is rendered as partial view).
$('#SearchedString').on('keyup', function () {
postForm();
});
</script>
To prevent your server beeing swamped with request while the user types, take a look at jQuery.debounce
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;
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.
I have an edit form which has a label and current values in textbox, I want to check if the values in the form has been changed when the form is submitted.
Here is the form
<fieldset>
<legend>Module <small>Edit</small></legend>
#using (Html.BeginForm("Edit", "Module"))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(m=>m.Id)
for(var i = 0; i < Model.Properties.Count(); i++)
{
<label class="label">#Model.Properties[i].Name</label>
<div class="input-block-level">#Html.TextBoxFor(model => Model.Properties[i].Value, new { #value = Model.Properties[i].Value })</div>
}
<div class="form-actions" id="buttons">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
this results to
How can i check if the form has been changed? My httppost method of controller currently look like this
[HttpPost]
public ActionResult Edit(EditModule module)
{
if (ModelState.IsValid)
{
_repository.SaveModuleEdits(module);
Information("Module was successfully edited!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Edit was unsuccessful, if the problem persists please contact admin!");
return RedirectToAction("ModuleList", "Module", new { area = "Hardware" });
}
}
It is fairly straight forward on the client side if you using something like Knockout. Here is an article that describes how to use Knockout for change tracking. This article uses a Knockout add-on called KoLite to make it even simpler.
One way to check if a value has changed from its original state (server side), is through HMAC mechanism.
Basically it generates a hash based on a string and secret key, and this hash is sent along with the form as a hidden field (http get), if the value is changed by the customer then the recalculation of the hash (http post) will be different from what is stored in the hidden field, then you know that someone change the value of that field.
This may be a little overworked but is one of the safest methods.
https://security.stackexchange.com/questions/20129/how-when-do-i-use-hmac
How to generate HMAC-SHA1 in C#?
I'm not so experienced using MVC. I'm dealing with this situation. Everything works well until call the HttpPost method where has all its members null. I don't know why is not persisting all the data on it.
And everything works well, because I can see the data in my Html page, only when the user submit the information is when happens this.
[HttpGet]
public ActionResult DoTest()
{
Worksheet w = new Worksheet(..);
return View(w);
}
[HttpPost]
public ActionResult DoTest(Worksheet worksheet)
{
return PartialView("_Problems", worksheet);
}
This is class which I'm using.
public class Worksheet
{
public Worksheet() { }
public Worksheet(string title, List<Problem> problems)
{
this.Title = title;
this.Problems = problems;
}
public Worksheet(IEnumerable<Problem> problems, WorksheetMetadata metadata, ProblemRepositoryHistory history)
{
this.Metadata = metadata;
this.Problems = problems.ToList();
this.History = history;
}
public string Title { get; set; }
public List<Problem> Problems { get; set; } // Problem is an abstract class
public WorksheetMetadata Metadata { get; set; }
public ProblemRepositoryHistory History { get; set; }
}
And my razor view.... the razor view shows successfully my view. I realized something rare, please note in my 5 and 6 lines that I have HiddenFor method, well if I used that, when calls HTTPPOST persists the data, I don't know why.
#model Contoso.ExercisesLibrary.Core.Worksheet
<div id="problemList">
<h2>#Html.DisplayFor(model => model.Metadata.ExerciseName)</h2>
#Html.HiddenFor(model => model.Metadata.ExerciseName)
#Html.HiddenFor(model => model.Metadata.ObjectiveFullName)
#for (int i = 0; i < Model.Problems.Count; i++)
{
<div>
#Html.Partial(Contoso.ExercisesLibrary.ExerciseMap.GetProblemView(Model.Problems[i]), Model.Problems[i])
</div>
}
</div>
UPDATE
I'm using a static class to get the view name, but as I'm testing I'm just using this Partial view
#model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1
<div>
<span style="padding:3px; font-size:18px;">#Model.Number1</span>
<span style="padding:5px; font-size:18px;">+</span>
<span style="padding:5px; font-size:18px;">#Model.Number2</span>
<span style="padding:5px; font-size:18px;">=</span>
<span style="font-size:18px">
#Html.EditorFor(model => model.Result, new { style = "width:60px; font-size:18px;" })
#Html.ValidationMessageFor(model => model.Result)
</span>
</div>
#section Scripts {
}
And here the user do the post
#model Contoso.ExercisesLibrary.Core.Worksheet
<form method="post">
#Html.Partial("_Problems", Model)
<input type="submit" value="Continue" />
</form>
The Model Binder will 'bind' or link input fields on your view to the model. It will not bind display fields (like label), that is why you need the HiddenFor it will add an <input type="hidden" which will then be bound to the Model when you Post.
You can use 'TempData'. It is used to pass data from current request to subsequent request means incase of redirection.
This link also helps you.
TempData
SO Tempdata
Make sure your form tag looks like the following, for instance the controller name, action method, the form method and an id for the form. I am referring to the #using statement. In my case the controller name is RunLogEntry, the action method is Create and the id is form.
Normal Post from View to Controller
#using (Html.BeginForm("Create", "RunLogEntry", FormMethod.Post, new { id = "form", enctype = "multipart/form-data" }))
{
<div id="main">
#Html.Partial("_RunLogEntryPartialView", Model)
</div>
}
If you want to post via Jquery, could do the following:
$.post("/RunLogEntry/LogFileConfirmation",
$("#form").serialize(),
function (data) {
//this is the success event
//do anything here you like
}, "html");
You must specify a form with correct attribute in your view to perform post action
<form action="Test/DoTest" method="post">
...
</form>
or
#using(Html.BeginForm("DoTest", "Test", FormMethod.Post)) {
...
}
The second is recommended.
Put your entire HTML code under:
#using(Html.BeginForm())
tag.