Using Javascript to validate an Apex form
Last Updated on Monday, 23 November 2009 04:45 Written by Steve Monday, 16 November 2009 02:15
Recently I came into a project where an Apex form on a Sites page had to validate itself in Javascript before sending an email via a controller method. I hadn’t ever done this before, so after some digging and some help, I thought I’d share the solution.
First, here is the VisualForce form:
<apex:form id="emailform" >
<input type="hidden" id="redirect" name="redirect" value="contactus">
Your Email: <apex:inputText value="{!youremail}" id="youremail" maxlength="80" size="40" /><br/>
Subject: <apex:inputText value="{!subject}" id="Subject" maxlength="80" size="40" /><br/>
Message: <apex:inputTextarea value="{!body}" id="Body" rows="6" cols="40"/><br/>
<apex:commandButton value="Send" id="sendButton" onclick="validate3()"/>
<apex:actionFunction id="emailSendFunction" name="sendEmail" action="{!send}">
<apex:param name="youremailparam" assignTo="{!youremail}" value=""/>
<apex:param name="subjectparam" assignTo="{!subject}" value=""/>
<apex:param name="topicparam" assignTo="{!topic}" value=""/>
<apex:param name="bodyparam" assignTo="{!body}" value=""/>
</apex:actionFunction>
</apex:form>
Note that the form doesn’t have an action: we’re invoking code via the onclick action of the command button. When the button is clicked, we’re calling Validate() a Javascript function on the page that looks like this:
function validate() {
var errorMessage = "Please fix the following errors: \n\n";
var errorFound = 0;
if (!isValidEmail(document.getElementById("about_contact:emailform:youremail").value)) { errorMessage += "Enter a Valid Email Address\n"; errorFound = 1; }
if (document.getElementById("about_contact:emailform:Subject").value.length == 0) {errorMessage += "Enter a Subject\n"; errorFound = 1; }
if (document.getElementById("about_contact:emailform:Body").value.length == 0) {errorMessage += "Enter your Message\n"; errorFound = 1; }
if (errorFound == 1) {
alert(errorMessage);
} else {
sendEmail();
}
}
It’s a simple validation scheme. Check a bunch of fields and if they don’t look right, add a message and pop it up to the user. If everything is successful, we call sendEmail().
SendEmail() is the tricky part. It’s actually an Apex controller method exposed as a Javascript function so that it can be called by Javascript. This is done via the ActionFunction tag in Visualforce. You can see it in the VisualForce form above, but I’ll reproduce it here:
<apex:actionFunction id="emailSendFunction" name="sendEmail" action="{!send}">
<apex:param name="youremailparam" assignTo="{!youremail}" value=""/>
<apex:param name="subjectparam" assignTo="{!subject}" value=""/>
<apex:param name="topicparam" assignTo="{!topic}" value=""/>
<apex:param name="bodyparam" assignTo="{!body}" value=""/>
</apex:actionFunction>
The controller’s send method is exposed and 4 parameters are asked for. Each of these parameters are mapped to public variables in the controller, where they are then used by the send method.
public class sffSendEmail {
public String youremail { get; set; }
public String subject { get; set; }
public String topic { get; set; }
public String body { get; set; }
public String redirect { get; set; } // Value tells us where to redirect
// Constructor
public sffSendEmail() {
}
public PageReference send() {
// Define the email
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] { 'destination@email.com' };
// Sets the paramaters of the email
email.setSubject( subject );
email.setToAddresses( toAddresses );
email.setPlainTextBody( 'From: ' + youremail + '\n\n\r' + 'Subject: ' + subject + '\n\n\r' + 'Message: ' + body + '\n\n\r' );
// Sends the email
Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});
PageReference secondPage = Page.confirmation_page;
secondPage.setRedirect(true);
return secondPage;
}
}
If you don’t use the param tags and explicitly map the form fields to the public variables, I found that the send method is invoked before the setters have run on your form, leaving you with null variables. Using the params gets the values into those variables for use.
Update: From the comments, Andrew Waite has a better way of constructing the validate() javaScript:
FYI, it’s a best practice to avoid the dependency on the generated DOM ID, i.e.: document.getElementById(”about_contact:emailform:youremail”. You’ve qualified every element in your path which is good but your code is still susceptible to being broken by a new layer in the hierarchy.
…
The better approach is to define your validation function to accept the DOM IDs as args and use $Component. For example, assume the validate function took one arg for the “youremail” field: function validate(emailid) {….}, and then your onclick would be something like: onclick=”validate({!$component.youremail});”.
Super post! It clarified something that has always confounded me: How do I pass arguments to a controller method?
I notice that the method send() takes no arguments. So the apex:param components set global variables, which the send method reads. That makes sense, but I wonder if it is (outside of Apex/Visualforce) a best-practice to use global variables instead of method arguments.
Is this a limitation of Apex/VF? If it is, you’ve written an excellent example of a workaround that I’d love to adapt for my own projects.
Thanks!
Glad you dug it!
Good thing you can locate the IDs generated by VF easily. That process still trips me up even though Apex is 10x’s better than ASP.NET when it comes to auto-generated ID syntax.
There is great documentation covering this feature (http://bit.ly/JwKp2), but if I know referencing elements will be required in the design, I generally will find a way to use plain old HTML elements and assign the IDs myself (just for debugging sanity).
I’m probably missing something very simple here, but I don’t understand why validation like this needs to happen in JS, as opposed to being done natively with apex & VF. Or was there just a preference to do it that way? If so, why?
Great example, Steve. But I wonder if you need the actionFunction callback in this case. Can’t you just let the Send button run an action=”{!save}” as usual if the JS validation succeeds? If the onclick function returns false, the action does not happen. This is what I use in web-to-lead forms, and I don’t see why it wouldn’t work in VF too.
I think I posted web to lead code that shows this a while back on nonprofitcrm dot org – I have not tried it in VF yet, but I expect it would work.
Great post Steve. Validation can also be done via the apex controller as noted by Sam Arjmandi: http://salesforcesource.blogspot.com/2008/09/how-do-custom-validations-in-visual.html but you have less control compared to javascript validation. For example, visualforce will place all the messages/errors in the component. There are also issues for required fields as the error text only appears next to input:fields and not for input:text, etc. It also displays a somewhat funky message in the . Apex validation errors will also only run after “field required” errors which can be annoying for users as they don’t see the error until all “field required” errors are eliminated.
Instead of calling actionFunction which calls the controller, we could validate in the JavaScript and depending upon its result, call the controller function. Heres a sample code
*****
function confirmation()
{
return confirm(‘Are you sure you want to delete record?’);
}
*****
If the user selects No, the controller action is never called. Also there is no need to specifically pass any arguments in param tag.
Missed
Missed the below in the above comment:
Missed the below in the above comment:
apex:commandLink action=”{!delRecord}” value=”Del” onclick=”if(!confirmation()) return false;”
Awesome ideas, guys, thanks. This was a rush project for me so I knew you all would have better ideas than me! Thanks for the great feedback.
Matthew. You’re right, there would be other ways to architect this one. I came in to someone elses code and was working with that.
Thanks for this post, Steve. FYI, it’s a best practice to avoid the dependency on the generated DOM ID, i.e.: document.getElementById(“about_contact:emailform:youremail”. You’ve qualified every element in your path which is good but your code is still susceptible to being broken by a new layer in the hierarchy.
I noticed an earlier comment about dropping to raw HTML tags which is one strategy but in this case you would also lose value binding to the controller which is a non-starter IMO.
The better approach is to define your validation function to accept the DOM IDs as args and use $Component. For example, assume the validate function took one arg for the “youremail” field: function validate(emailid) {….}, and then your onclick would be something like: onclick=”validate({!$component.youremail});”.
Awesome Andrew! Thanks for the info. I’ll update the post to reflect this better way of doing things.
Hi,
tnx for sharing this!!
BTW, I had to include “return false” in the commandButton onClick attribute in order to not refresh the entire page:
Is that correct? I mean, whitout it my VF page was not working correctly (refreshing the entire page), since I’m using AJAX to handle the data form and refreshing just a “Record saved” label.
Best regards