Friday 29 April 2011

Field Level Error Messages with Visualforce - Part 2

In Part 1 of this topic, we looked at how to add field level error messages to input fields on a Visualforce page.  Things are a little more complicated if you aren't using input fields on the page - for example, if you are using input text components backed by properties from a wrapper class.  In this situation, the styling and conditional rendering of error messages must be handled by the page.

As we want to replicate the behaviour when using input fields, the first thing to look at is the styling of errors on input fields.  Viewing the source of the page shows that the style class of the input is changed to 'error', which places a red border around the input box.  This is followed by a div containing the error message:

<div class="errorMsg"><strong>Error:</strong> Either email or phone must be defined</div>

So this should be pretty straightforward to replicate.  First, we need a wrapper class where each field can have an associated error message.  Note that this is by no means the most efficient or elegant wrapper class for this purpose, but it makes it easier to understand what is happening on the page.

public class Wrapper
{
 public String FirstName {get; set;}
 public String MiddleName {get; set;}
 public String LastName {get; set;}
 public String Email {get; set;}
  
 public String FirstNameError {get; set;}
 public String MiddleNameError {get; set;}
 public String LastNameError {get; set;}
 public String EmailError {get; set;}
  
 public Wrapper()
 {
  FirstNameError='';
  MiddleNameError='';
  LastNameError='';
  EmailError='';
 }
}

The validation in this case is that one of first or middle name must be defined, so the validation in the save method is:

for (Wrapper wrap : wrappers)
{
 if ( ((null==wrap.FirstName) || (wrap.FirstName.trim().length()==0)) &&
      ((null==wrap.MiddleName) || (wrap.MiddleName.trim().length()==0)) )
 {
  wrap.FirstNameError='Either first name or middle name must be defined';
  wrap.MiddleNameError='Either first name or middle name must be defined';
 }
}

Finally, the Visualforce markup to display the error if necessary:

<apex:column headerValue="First Name"> 
   <apex:inputText value="{!wrapper.FirstName}" rendered="{!LEN(wrapper.FirstNameError)==0}"/>
   <apex:outputPanel rendered="{!LEN(wrapper.FirstNameError)!=0}">
     <apex:inputText styleClass="error" value="{!wrapper.FirstName}"/>
     <div class="errorMsg"><strong>Error:</strong>&nbsp;{!wrapper.FirstNameError}</div>
   </apex:outputPanel>  
</apex:column>

If the length of the error message associated with the wrapper property is zero (i.e. there is no error), then the input component is displayed as normal.  However, if the length of the error message is greater than zero, a composite output panel is displayed that styles the input component as an error and the error message is output beneath.

I'm duty bound to point out that using the Salesforce styles 'error' and 'errorMsg' is not officially recommended, as Salesforce may choose to change their styling at any point in time.  If I were to use this in a production system, I'd recreate (i.e. copy and paste!) the Salesforce styles in my own CSS include.

And here's the page displaying the error messages in the desired style:

13 comments:

  1. Hi Bob,
    In save method I kept to validate a firstname field to enter only characters & need to display an error message when wrong value enter. I am not getting the message in the vf page for input text field

    String Regex = '([^a-zA-Z])';
    Pattern MyPattern = Pattern.compile(Regex);
    Matcher MyMatcher = MyPattern.matcher(FirstName);
    if (MyMatcher.matches()) {
    // Raise an error
    FirstNameError='name will allow only letters';
    }

    ReplyDelete
  2. Have you added some debug to your controller to ensure that the error is being trapped - i.e. that the firstnameerror field is getting populated?

    ReplyDelete
  3. Huge help, thanks Bob! I actually rolled my own cell-level error messaging a while back by adding several fields to the object. This is so much easier. Cheers!

    ReplyDelete
  4. You could use !String.isBlank(...) for readability instead of (null!=...) && ...trim().length()

    ReplyDelete
  5. HI Bob I am new to salesforce can u let me know run the above in apex and vf page

    ReplyDelete
    Replies
    1. You'll need to finish the page and class first - they are just snippets above. The way I used them was to query back a list of contacts and then create a wrapper class instance per contact.

      Delete
  6. Bob how to send error message through javascript.

    ReplyDelete
  7. Bob in this every time request going to controller and error message coming from there , But if we want to implement in vf page itself is it possible( I mean client side validation by using java script )

    ReplyDelete
    Replies
    1. It is. There's an example of this in my book using the jQuery validation plugin.

      Delete
  8. Good post, really helpful workaround to an issue I had with inputs using the required attribute, namely, I wasn't able to partially refresh a page to update the form when some of the inputs were required, and did not have a value in them- and should not have yet. Thanks for the post!

    ReplyDelete
  9. Awesome post Bob, but what if I am using apex:pageBlockSectionItem and want to show the error message on the field using in it.
    As of now we can use only have no more than 2 child components in apex:pageBlockSectionItem.

    ReplyDelete
    Replies
    1. working with that also , thanks for posting

      Delete