JavaScript Validation in a Visualforce Page
When I started programming using web frameworks like Visualforce, it wasn’t entirely clear what was happening on the server vs the client web browser. I routinely see questions about this in the Force.com developer forums, so I created a simple example to illustrate the round trip from the server, to the browser, to the server and back.
Here is a VF page. It displays a table of information about the Users. It also has a text input field, an HTML button, and an output field. When you enter a value in the input field and click Validate, a popup dialog displays the text, then refreshes part of the page putting the entered text in the Text Field page block. The entered text is read and processed by the JavaScript function checkField first. If it passes validation, in this case a simple check for null, then it is sent to the server via an actionFunction. Even though the same field is bound in both places, why doesn’t it update in the outputText (2) after I type it in the inputText (1)? The answer lies in the page lifecycle as I will explain below. Here is the page:
We entered some text, clicked Validate:
Voila! The outputText shows the text:
Now lets look at the code. First the VF. The page uses a standard controller for the User object and a controller extension. There is an apex:pageBlockTable on line 19 for listing the details of the Users. The field testField is bound in 2 places, lines 29 and 38. When the page is initialized, the apex:inputText and apex:outputText show the value from the server, null. When you type some text in the inputText, it is just sitting there in the browser. Its bound to a variable in the controller, but until the form is submitted, the server won’t be updated. I could have submitted it to the server with an apex:commandButton, but I used an HTML button to show that in the browser VF tags are really just your standard HTML/CSS/JavaScript. When we click the button on line 30, we call the JavaScript function on line 4. After a check for null, we finally send the entered text to the server using an apex:actionFunction on line 31. The server side variable is updated with the entered text, returned to the server, and the apex:outputPanel on line 35 gets re-rendered. Finally the apex:outputField displays the text we entered.
1 <apex:page standardController="User" extensions="HelloWorldUsers_ConExt" recordSetVar="users"> 2 <script> 3 // JavaScript function to check the value entered in the inputField 4 function checkField() { 5 var val = document.getElementById('{!$Component.theForm.thePageBlock.theFieldInput}').value; 6 if (val != null && val != '') { 7 alert('val is '+val); 8 // call the actionFunction, sends the value to the server 9 fieldUpdateFunction(val); 10 } 11 else alert('Please enter some text'); 12 } 13 </script> 14 15 <apex:form id="theForm" > 16 17 <apex:outputPanel id="phoneListPanel" > 18 <apex:pageBlock title="Phone List"> 19 <apex:pageBlockTable value="{!users}" var="user"> 20 <apex:column value="{!user.Name}" /> 21 <apex:column value="{!user.Phone}" /> 22 <apex:column value="{!user.MobilePhone}" /> 23 <apex:column value="{!user.Fax}" /> 24 </apex:pageBlockTable> 25 </apex:pageBlock> 26 </apex:outputPanel> 27 28 <apex:pageBlock id="thePageBlock" title="Test Validation"> 29 <apex:inputText id="theFieldInput" value="{!testField}" /> 30 <input type="button" value="Validate" onclick="checkField();" /> 31 <apex:actionFunction name="fieldUpdateFunction" 32 action="{!fieldUpdate}" rerender="phoneListPanel,fieldPanel" /> 33 </apex:pageBlock> 34 35 <apex:outputPanel id="fieldPanel"> 36 <apex:pageBlock id="anotherPageBlock" title="Text Field"> 37 <apex:pageBlockSection > 38 <apex:outputText label="Field value from server" value="{!testField}" /> 39 </apex:pageBlockSection> 40 </apex:pageBlock> 41 </apex:outputPanel> 42 43 </apex:form> 44 45 </apex:page>
There is not much to it, but lets take a look at the server code, the controller extension:
1 // Controller Extension for HelloWorldUsers 2 public with sharing class HelloWorldUsers_ConExt { 3 4 // Field to bind to the inputText component 5 public String testField {public get; public set;} 6 7 // Simple contructor 8 public HelloWorldUsers_ConExt(ApexPages.StandardSetController stdController) { 9 } 10 11 // Method called by the actionFunction on the HelloWorldUsers page 12 public PageReference fieldUpdate() { 13 System.debug('=============>>> testField: '+testField); 14 15 // return null to keep us on the page 16 return null; 17 } 18 19 }
We have a String textField on line 5 that is bound to the inputText and outputText. The method fieldUpdate is called by the actionFunction. On line 16 it returns null so that when it returns to the browser it stays in the same page.
Knowing the difference between the server and browser begs the question: do you want to do the processing in the browser or on the server? Typically you want to do what you can in the browser. Its ideal to do simple validation, like name/address/phone format, especially if you don’t need to connect with another service. You can do more on the server, but it takes a least one round trip so its usually slower. A complex integration may involve AJAX to the server, connecting with RESTful web services, and lots of other fun machinations that are beyond the scope of this post.
I hope this article helps illustrate the difference between the server and the browser. The minutia of programming is hard enough, so its critical to understand the bigger picture like the lifecycle of a Visualforce page.