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});”.


15 Comments

  1. David Schach   |  Monday, 16 November 2009 at 2:46 pm

    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!

  2. Steve   |  Monday, 16 November 2009 at 3:23 pm

    Glad you dug it!

  3. Mike Leach   |  Monday, 16 November 2009 at 6:17 pm

    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).

  4. Matthew   |  Monday, 16 November 2009 at 7:42 pm

    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?

  5. Evan   |  Monday, 16 November 2009 at 8:27 pm

    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.

  6. Tanner   |  Tuesday, 17 November 2009 at 8:01 am

    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.

  7. Rajesh   |  Wednesday, 18 November 2009 at 2:16 am

    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.

  8. Rajesh   |  Wednesday, 18 November 2009 at 2:17 am

    Missed

  9. Rajesh   |  Wednesday, 18 November 2009 at 2:18 am

    Missed the below in the above comment:

  10. Rajesh   |  Wednesday, 18 November 2009 at 2:19 am

    Missed the below in the above comment:

    apex:commandLink action=”{!delRecord}” value=”Del” onclick=”if(!confirmation()) return false;”

  11. Steve   |  Wednesday, 18 November 2009 at 10:45 am

    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.

  12. Steve   |  Wednesday, 18 November 2009 at 10:47 am

    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.

  13. Andrew Waite   |  Monday, 23 November 2009 at 4:40 pm

    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});”.

  14. Steve   |  Monday, 23 November 2009 at 4:41 pm

    Awesome Andrew! Thanks for the info. I’ll update the post to reflect this better way of doing things.

  15. Paulo Castro   |  Tuesday, 29 December 2009 at 11:20 am

    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

Leave a Reply