Category Archives: jQuery

DataTables in Visualforce, part 1

I’ve been doing a lot of work with jQuery DataTables. They can add a nice custom interface for your Salesforce data with very little coding. I’m going to devote the next few posts on ways to use DataTables in your Visualforce pages. In this first post, we’ll make it pretty simple using a standard html table created using an <apex:repeat> tag and then using jQuery to make the table into a DataTable with zero configuration. Then we’ll do a couple of extra things to make our table a little more functional.

First, we need a controller that gives us the data we need. Our example will be listing contacts in Salesforce.

Now, here is a Visualforce page that turns that ugly table into a nice looking DataTable with sorting and filtering, plus some styling. Pretty easy, right?

Alright, let’s get a little fancy. We want to have a drop down listing all the accounts so we can filter by account name. We also want to default the order of the rows by contact last name. To do the sorting, we specify order with an array. The columns are zero-based, so last name which is the 3rd column will be index 2.

To add the drop down, we use jQuery to get the unique values from the first column, sort them and then append them to a select component on the page. We also add a handler to the select component to filter when the value changes. If “All” is selected, then it clears the filter.

And here’s what it looks like.datatables1

In the next post, I’ll show how to leverage Visualforce remoting to add some dynamic features to a DataTable.

 

Using jQuery to Validate User Input

This week on Twitter, @lesteb¬†posted a question about the best way to validate if a user had checked one of two checkboxes on a Visualforce page. We went back and forth for a while about his requirements. Since Salesforce doesn’t have radio buttons and it is impossible to make a checkbox field required, we needed some other way to make sure his requirements were met. I had a couple of ideas.

My first thought was to use Validation Rules – this is a great button click feature of Salesforce and, as Nick Hamm so eloquently pointed out recently on his blog, we should try to use configuration first and code second. In this case Bryan couldn’t use validation rules because he only wanted it to validate on one page and wanted to have more control of the error message.¬†Our next option to look at was putting in some logic in the controller extension save method. Again, Bryan ruled this out because he wanted more control over the error message.

So, I then suggested using Javascript to do some validation with help from jQuery. In Visualforce, we can use onClick to run some JavaScript before the Apex method assigned to the button is executed. In the example below, the beforeSave() function is called first and only if that function returns true will it go to the Apex method mySave.

[code]<apex:commandbutton value="Save" id="saveBtn" action="{!mysave}" onClick="if(!beforeSave()) return false;"/>[/code]

The JavaScript function beforeSave is below. It uses jQuery to check the values of the the two checkboxes. At least one must be checked for the function to return true. If neither one is checked, it opens a jQuery UI dialog box telling the user to check a box. I’ll get into that in a minute.

[code]
function beforeSave() {
if (!j$(‘[id$=checkbox1]’).attr(‘checked’) && !j$(‘[id$=checkbox2]’).attr(‘checked’)) {
j$( "#dialog-message" ).dialog(‘open’);
return false;
} else
return true;
}
[/code]

Now for the dialog box. First we need a div on the page that will be used for the message:

[code]
<div id="dialog-message" title="Info Needed">
<p>
<span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 50px 0;"></span>
You should check a box!
</p>
</div>
[/code]

Once we have the div, we need to turn it into a jQuery UI Dialog. Here’s the way to do that:

[code]
j$(document).ready(function() {
j$("#dialog-message").dialog({
autoOpen: false,
modal: true,
buttons: {
Ok: function() {
j$( this ).dialog( "close" );
}
}
});
});
[/code]

Bryan had one more requirement. Only one of the two checkboxes could be checked. I turned to a little more JavaScript to make this work. Here’s the visualforce markup where I added an onClick call to a JavaScript function to the input field.

[code]
<apex:inputField value="{!account.Checkbox1__c}" id="checkbox1" onClick="deselectOther(this);"/>
[/code]

The JavaScript function to deselect one checkbox when the other gets checked is pretty simple. It gets the element clicked passed in, compares the id of that element and then use jQuery to find and uncheck the other one.

[code]
function deselectOther(e) {
if (e.id.match(/checkbox1$/))
j$(‘[id$=checkbox2]’).attr(‘checked’, false);
if (e.id.match(/checkbox2$/))
j$(‘[id$=checkbox1]’).attr(‘checked’, false);
}
[/code]

Bryan did a nice little demo video of his nearly finished page. Note that this video was done before the final trick of deselecting one checkbox when the other was selected. He was handling this case with an error message.

I’ve created an unmanaged package that you can install into your org to see the entire sample code. It doesn’t match Bryan’s video entirely as he did a bunch of other messages and validations, but I hope it points you in the right direction. As always, I’d love to hear thoughts on how I could improve this, or maybe you have a completely different way to do it!

Salesforce Javascript Remoting, jQuery and Autocomplete

I was very excited when I found out that Salesforce was releasing Javascript remoting with Spring 11. There have been several cases where having access to Apex classes and logic in Javascript would make my development much easier. One area that I wanted to try out was in an autocomplete component that I built a while ago. I had hacked together a component that used a jQuery autocomplete plugin. While it worked, I had a couple of problems: it didn’t work in IE8 (everybody uses Chrome or Firefox, right?) and the autocomplete consumed a separate Visualforce page, so when I had developer mode turned on, it would choke on the wrapper that developer mode uses.

Using Josh Birk’s blog post and the Salesforce documentation as a reference, I got to work.

First I created the controller. I made this controller as flexible as possible. You can pass it the API object name (Account, Contact, etc), the search string and any additional fields you want to include in the search. It will always search the Name field for the search string.

[Edit: I have created a new controller that uses apex-lang to build the Soql. You can find it in my blog post: http://verticalcode.wordpress.com/2011/02/21/using-apex-lang-to-build-soql-statements/.]

global class autoCompleteController {
    @RemoteAction
    global static SObject[] findSObjects(string obj, string qry, string addFields) {
        // more than one field can be passed in the addFields parameter
        // split it into an array for later use
        List<String> fieldList;
        if (addFields != null) fieldList = addFields.split(',');
       // check to see if the object passed is valid
        Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
        Schema.SObjectType sot = gd.get(obj);
        if (sot == null) {
            // Object name not valid
            return null;
        }
        // create the filter text
        String filter = ' like '%' + String.escapeSingleQuotes(qry) + '%'';
        //begin building the dynamic soql query
        String soql = 'select id, Name';
        // if an additional field was passed in add it to the soql
        if (fieldList != null) {
            for (String s : fieldList) {
                soql += ', ' + s;
            }
        }
        // add the object and filter by name to the soql
        soql += ' from ' + obj + ' where name' + filter;
        // add the filter by additional fields to the soql
        if (fieldList != null) {
            for (String s : fieldList) {
                soql += ' or ' + s + filter;
            }
        }
        soql += ' order by Name limit 20';
        List<sObject> L = new List<sObject>();
        try {
            L = Database.query(soql);
        }
        catch (QueryException e) {
            return null;
        }
        return L;
   }
}

Next I created the component. Note that the component requires some jQuery resources which are being loaded from Google’s CDN for the jquery files.

I also chose to use a spinner type indicator to queue the user that the autocomplete is working, which I uploaded as a resource. You can generate your own gif spinner at http://ajaxload.info/.

<apex:component controller='autoCompleteController'>
  <!-- JQuery Files -->
  <script src='https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js'/>
<script src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.12/jquery-ui.min.js'/>
<apex:stylesheet value='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.12/themes/ui-smoothness/jquery-ui.css'/>
  <!-- Attributes Required For Component -->
  <apex:attribute name='objectname' description='The object name you want to look for.'     type='String' required='true'/>
  <apex:attribute name='additionalfield' description='Any additional fields you'd like to search and include in the display.'     type='String' required='false'/>
  <apex:attribute name='autocomplete_textbox' description='The ID for the Autocomplete List Textbox.'     type='String' required='true'/>
  <style>
    .ui-autocomplete-loading { background: white url({!$Resource.circleIndicator}) right center no-repeat; }
  </style>
  <script type='text/javascript'>
    var j$ = jQuery.noConflict();
    j$(document).ready(function() {
 
        var sObjects;
        var queryTerm;
 
        j$(esc('{!autocomplete_textbox}')).autocomplete({
            minLength: 2,
            source: function(request, response) {
                        queryTerm = request.term;
                        autoCompleteController.findSObjects('{!objectname}', request.term, '{!additionalfield}', function(result, event){
                            if(event.type == 'exception') {
                                  alert(event.message);
                            } else {
                                 sObjects = result;
                                 response(sObjects);
                            }
                        });
                   },
            focus: function( event, ui ) {
                    j$(esc('{!autocomplete_textbox}')).val( ui.item.Name );
                    return false;
                    },
            select: function( event, ui ) {
                        j$(esc('{!autocomplete_textbox}')).val( ui.item.Name );
                        j$(esc('{!autocomplete_textbox}_lkid')).val( ui.item.Id );
                        j$(esc('{!autocomplete_textbox}_lkold')).val( ui.item.Name );
                        return false;
                    },
         })
         .data( 'autocomplete' )._renderItem = function( ul, item ) {
            var entry = '<a>' + item.Name;
            j$.each('{!additionalfield}'.split(',') , function(key, value) {
                entry = entry + ' ' + item[value];
            });
            entry = entry + '</a>';
            entry = entry.replace(queryTerm, '<b>' + queryTerm + '</b>');
            return j$( '<li></li>' )
                .data( 'item.autocomplete', item )
                .append( entry )
                .appendTo( ul );
        };
    });
 
    function esc(myid) {
           return '#' + myid.replace(/(:|.)/g,'\\$1');
    }
 
  </script>
</apex:component>

Finally, I can use the component in any Visualforce page to autocomplete on any field. In the below example, I’m looking for account name or ticker symbol:

    <apex:page>
    <apex:form><br />
        <apex:inputText id='account'>
            <c:AutoComplete2 objectname='Account' additionalfield='TickerSymbol' autocomplete_textbox='{!$Component.account}' />
        <apex:inputText>
    </apex:form>
</apex:page>