Hello I'm trying to get my contact form to display a thank you message after the message has been sent.
I looked around but the stuff I find looks more complex than what I think i need.
I think it's something i'm missing about the event listener and how this form works.
Here's my code:
.thanks {
display: none;
}
<div class="form-wrap">
<form class="contact-form" action="https://getsimpleform.com/messages?form_api_token=aa0a1c58e87ea8816ba9ff7d7a71d0ef" method="post">
<!-- all your input fields here.... -->
<div class="name-email">
<input class="contact-field contactform-name" type='text' name='name' placeholder="Name" required/>
<input class="contact-field contact-form-email" type="email:" name="email" placeholder="e-mail" value="" required>
</div>
<textarea class="contact-field" name="message" rows="20" cols="80" placeholder="Your Message" required></textarea>
<input class="contact-field submit" type='submit' value='Send' />
</form>
</div>
<div class="thanks">
<h1>Thanks for the message!</h1>
</div>
<script>
function displayThanks() {
document.querySelector(".thanks").style.display = "block";
document.querySelector(".contact-form").style.display = "none";
}
document.querySelector(".submit").addEventListener("submit", displayThanks)
</script>
I could make it work on click, but that would mean that even if they don't send a message and just click submit they will get thank you (FOR WHAT!?)
Thanks!
M
You are mixing client side logic with server side logic. It's hard to answer your question because we don't know, what the action "action="https://getsimpleform.com/messages?form_api_token=aa0a1c58e87ea8816ba9ff7d7a71d0ef" in your form tag is doing.
It might be usefull to validate the form, before you consider to submit it. You can do this by using the click event of the submit button in combination with preventDefault like this (Just an Example, you could even use RegEx for emails and stuff):
document.querySelector(".submit").addEventListener('click', function(e) {
var userInputName = document.getElementsByName("name")[0].value,
userInputEmail = document.getElementsByName("email")[0].value,
userInputMessage = document.getElementsByName("message")[0].value;
if (userInputName.length > 0 && userInputEmail.length > 0 && userInputMessage.length > 0)
{
document.querySelector(".thanks").style.display = "block";
document.querySelector(".contact-form").style.display = "none";
}
else
{
e.preventDefault();
}
}, false);
I would recommend to use IDs instead of names though, because IDs are unique and don't require a node list or ambiguous jQuery. Than you can use something like this: userInputName = document.getElementById("name").value;
And don't forget to use the right CSS for your logic. Like:
.thanks {display: none;}
I am using a template based form in angular. I also use bootstrap (v4) and I wish to show some validation messages when the form was submitted.
This is my form:
<form [ngClass]="{'was-validated': wasValidated}">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" class="form-control" [(ngModel)]="category.name" #name="ngModel" required maxlength="100"/>
<div *ngIf="name.invalid" class="invalid-feedback">
<div *ngIf="name.errors.required">
Name is required.
</div>
</div>
</div>
<button type="submit" class="btn btn-success" (click)="save()">Save</button>
</form>
My component looks as follows:
category: Category;
wasValidated: boolean = false;
ngOnInit() {
this.reset();
}
save() {
this.wasValidated = true;
this.categoriesService.createCategory(this.category).subscribe(
() => {
this.notificationService.add(notifications.category_saved, {name: this.category.name});
this.reset();
},
() => this.notificationService.add(notifications.save_category_failed)
);
}
reset() {
this.wasValidated = false;
this.category = {} as Category;
}
This works, but I have a feeling it's overly complex and more like a workaround rather than the right way. What is the best way to accomplish this?
Note: the class was-validated must be present on the form element in order to show the div with class invalid-feedback. I'm using this: https://getbootstrap.com/docs/4.0/components/forms/#validation
Note 2: I have currently no mechanism yet to prevent form submission on error. I'd like to know a good solution for that as well!
With the answer from #Chellappan V I was able to construct the solution I wanted.
I have applied to following changes:
First added #form="ngForm" to the form tag in the template. Secondly I changed the ngClass expression to reference the submitted state of the form, rather than referring to a boolean which was set to true manually when form was submitted. Last but not least I pass the form in the submit method on the save button.
<form novalidate #form="ngForm" [ngClass]="{'was-validated': form.submitted}">
<!-- form controls -->
<button type="submit" class="btn btn-success" (click)="submit(form)">Save</button>
</form>
In the component I injected the template variable in the component with #ViewChild.
#ViewChild("form")
private form: NgForm;
The submit method now takes a form parameter of type NgForm which is used to check if the form was valid before sending a request to the backend:
submit(form: NgForm) {
if (form.valid) {
this.categoriesService.createCategory(this.category).subscribe(
() => {
this.notificationService.add(notifications.category_saved, {name: this.category.name});
this.reset();
},
() => this.notificationService.add(notifications.save_category_failed)
);
} else {
this.notificationService.add(notifications.validation_errors);
}
}
Finally the reset method resets the form and the model so it can be re-entered to submit a next instance:
reset() {
this.form.resetForm();
this.category = {} as NewCategoryDto;
}
Trying to implement data validation and error handling on a simple contact form. When I add the check for ModelState.IsValid I'm in a chicken and egg situation. I have looked at other similar questions and am just not getting this. Moving from Web Forms to MVC and struggling. Trying to toggle HTML elements based on what's happening - success/error message, etc. RIght now, not even the validation is working.
Right now I'm just trying to get server-side validation working but would welcome advice on how to add client-side validation also; for example, is it necessary to use jQuery for this or is there something baked in?
View:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
if (ViewData["Error"] == null && ViewData["Success"] == null)
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
<input type="text" name="email" placeholder="Email..."
class="contact-email" id="contact-email">
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
<input type="text" name="subject" placeholder="Subject..."
class="contact-subject" id="contact-subject">
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
<textarea name="message" placeholder="Message..."
class="contact-message" id="contact-message"></textarea>
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
else if (ViewData["Error"] == null && ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
else if (ViewData["Error"] != null)
{
<h3>Oops!</h3>
<p>
We apologize. We seem to be having some problems.
</p>
<p>
Please come back and try again later. Alternatively,
call us anytime at (xxx) xxx-xxxx.
</p>
}
}
Model:
public class ContactModel
{
[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
[Required(ErrorMessage = "Subject is required")]
public string Subject { get; set; }
[Required(ErrorMessage = "Message is required")]
public string Message { get; set; }
}
Controller:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ViewData["Error"]
= "Something went wrong - please try again later.";
return View("Error");
}
}
return View();
}
Error View:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
UPDATE - 05/09/2017
Per Guruprasad's answer, if ModelState.IsValid evaluates to false, then no validation error messages are being reported on the form.
Note I had to change the AddModelError signature to not use the "Extension ex" parameter:ModelState.AddModelError("Error", "Server side error occurred"); as I do not want system errors being reported to users.
Note also that at this point I am only trying out validation on the server side (have yet to work through client-side validation).
I have updated the Contact.cshtml view as follows as no model errors were being displayed - I have included the Bootstrap .has-error and .help-block CSS rules for the validation errors:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group has-error">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email",
placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(model => model.Email, String.Empty,
new { #class="help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text",
name = "subject",
placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(model => model.Subject, String.Empty,
new { #class = "help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message",
placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(model => model.Message, String.Empty,
new { #class = "help-block" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to
call us at (xxx) xxx-xxxx at any time.
</p>
}
}
There are multiple things you need to understand here. Let me go point by point.
Its good to know that you have your model designed, but how your view gets to know that it has a model to bind for itself and when posting the form contents, how would server comes to know that, there is a model to be received. So on the first instance, you need to construct your view binding the model. To bind a model in a view, you need to first get a reference/declare it at the top, letting view know that, ok, here is a model for you to generate my view.
Well, you have ValidationSummary to true, then I would suggest that, instead of using ViewData to pass error message, you can use ModelState.AddModelError and let ValidationSummary take care of that. As a side note, you might also want to take care of this issue and you can resolve the same with answers mentioned in the same post. If you are not using or do not want to use Html.ValidationSummary, then you can stick to your current view.
Now, to display Success message, you can either use TempData or ViewData and follow the same structure as you have in your view now. Here is one more post to let you work on that.
Last and most important on View part is binding model properties to View elements. Use Razor View extension helpers to generate View for your model. You have #Html.TextBoxFor,#Html.TextAreaFor etc., You also have #Html.TextBox, #Html.TextArea which is not for binding model properties, but just to generate a plain HTML view. You can add other html properties within these helpers as shown in the updated view below. I would suggest to dig down more on the overloads available for these helpers.
So here is your updated view.
#model SOTestApplication.Models.ContactModel #*getting model reference*#
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#*Usage of helpers and html attributes*#
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
Controller Side validation
Not much to say on this part as it looks good. But based on few of my points above, I would suggest you to add ModelState.AddModelError instead of using ViewData for error messages. Eliminate your if conditions in view, so that contact form remains, even after postback. Now if you want to persist the values after server side validation, then just pass back the model to your view in your post method. Updated Controller would be:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ModelState.AddModelError("Server side error occurred", ex.Message.ToString());
}
}
return View(contactModel); //this will persist user entered data on validation failure
}
Client Side Validation
As far as this portion is considered, you have few more things to set up in your application.
You need to add Html.EnableClientValidation(true); and Html.EnableUnobtrusiveJavaScript(true); to your application. There are various possible ways to add this. You can add this on Web.config file under appSettings for global implication Or you can add this in particular view as mentioned in below updated View example.
Global Implication in Web.Config ex:
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
If you have noticed your BundleConfig.cs file under App_Start directory, you would have seen below entries created by default. These are the jquery stuffs responsible for your Client Side validation.
jQuery and jQueryVal entries
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
Next Step is to add reference to these files/use #section Scripts to render these bundles either in _Layout.cshtml or in any specific view. When you include this in _Layout.cshtml. these scripts/bundles are rendered wherever you use this layout with other views. So basically, its your call on where to render these.
For example here, I would render these in Contact.cshtml view soon after adding reference to model.
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
One Last thing to make this work here is that you need to use #Html.ValidationMessageFor razor extension and let MVC do the binding of error messages on particular properties. Also for these error messages to be displayed in the View, you need to specify ErrorMessage for each property in your model as you are doing it now with Required(ErrorMessage=... for each properties in model. There are more to know about these stuffs if you explore it in detail.
Your updated view with proper validations added.
#model SOTestApplication.Models.ContactModel
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
#using (Html.BeginForm("Contact", "Contacts", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(m => m.Message)
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
}
Hope I have clarified most of your doubts with these points. Happy Coding.. :)
Here is some code on my ASP.NET site. I initially wanted to write
if((n%4)==0) {
<div>
}
//code
if((n%4)==0) {
</div>
}
but ASP.NET wouldn't have it. It had a compile error. It appears it ignores } until I close the div. So I ended up with the below. The if statement causes me the same problem. I know I can conditionally have values by writing class="#(cond?"val":"")" but that only works for values I don'
t know how to conditionally have checked in there. Having the same line written 4 times is pretty ridiculous how do I write this properly?
<div>
#for (int i = 1, n = 0; i < 32; i <<= 1, ++n)
{
if ((looking & i) != 0)
{
<input type="checkbox" name="SomeName" value="#i" id="SomeName_#i" checked>
}
else
{
<input type="checkbox" name="SomeName" value="#i" id="SomeName_#i">
}
<label for="SomeName_#i">#TestApp.Controllers.HomeController.enumFriendlyName[n]</label><br />
}
</div>
<div>
#for (int i = 32, n = 4; i < 256; i <<= 1, ++n)
{
if ((looking & i) != 0)
{
<input type="checkbox" name="SomeName" value="#i" id="SomeName_#i" checked>
}
else
{
<input type="checkbox" name="SomeName" value="#i" id="SomeName_#i">
}
<label for="SomeName_#i">#TestApp.Controllers.HomeController.enumFriendlyName[n]</label><br />
}
</div>
I assume you are talking about razor here. I'll start with attributes.. the trick is to use the html helper rather than writing the html by hand.
#Html.Label("Name", "LabelText")
#Html.CheckBox("Name", isChecked)
Also, for the CheckBox and other helpers you can pass and htmlAttributes anon function, if the value you specify for the attribute evals to null, the attribute won't be included in the rendered html.
What are you doing with the divs? Perhaps if you tell us what you want to do we can suggest something. Are you trying to stripe, because you can do that with CSS.
So, rather than the code duplication you could possible use a partial as the content for each div:
<div class="left">
#Html.Partial(_controls, model)
</div>
<div class="right">
#Html.Partial(_controls, model)
</div>
Or something. I may have the syntax wrong, but basically you can pass a model to your partial which can contain the range of items you want.
The model can be like new { start = 1, finish = 32 }
How can I pass data from partial view on submit form in ASP.NET MVC.
#using (Html.BeginForm("Edit", "BlogPost", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
................
#Html.Partial("PostImagesForPost",Model.PostImages)
}
PostImagesForPost - partial view:
#model IEnumerable<Blog.Models.PostImage>
<script type="text/javascript" src="~/Scripts/jquery.zoom.min.js"></script>
<div>
#{
List<Blog.Models.PostImage> images = Model.ToList();
<ul class="images">
#foreach (var img in images)
{
string parameterValue_small = "~/BlogPhotos/120/" + img.Photo.ToString();
string parameterValue_big = "~/BlogPhotos/600/" + img.Photo.ToString();
<li>
<div id="jquery-image-zoom-example">
<span data-postid="#img.ID" data-delete="true" class="deletespan"></span>
<a href="#Url.Content(parameterValue_big)">
<img src="#Url.Content(parameterValue_small)" data-postid="#img.ID" class="zm" onclick="$('.jquery-image-zoom img').click()" />
</a>
<input type="checkbox" checked="checked" name="selectedImagesForDelete" style="display:none;" data-postid="#img.ID" value="#img.ID" />
</div>
</li>
}
</ul>
}
On submit function the parameter selectedImagesForDelete is null.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Post post,string[] selectedImagesForDelete)
{...........}
This has nothing to do with the fact you're using a partial, and everything to do with how the modelbinder in MVC works. For iterable posted items, the model binder expects field names in the form of ListProperty[index].ModelProperty. The problem is that the Html.* family of helpers will not create this name properly unless they are passed an indexed value, which you can't achieve with foreach. The solution is to simply use for, instead:
#for (var i = 0; i < images.Count(); i++)
{
Html.EditorFor(m => image[i].SomeProperty)
}
By passing in a value that's indexed (images[i]), the helper recognizes that it needs to add the proper indexed html prefix to the name, so that the modelbinder will understand where to stuff the value when it's posted back.
Though, in your case, you seem to actually just be manually specifying the HTML for the fields, which is fine, but you're responsible at that point for getting the name values right.
I believe your name property needs to have indexes in the name:
Create a index variable called index and increment it after each iteration
<input type="checkbox" name="selectedImagesForDelete[index]" value="2">
Actually it was a problem with the javascript file. The checkboxes were never checked.
<input type="checkbox" name="selectedImagesForDelete" value="#img.ID" />
But I resolved that problem and now everything works like expected.
But thanks for trying to help me. I appreciate it.