Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Thursday, May 5, 2011

Lazy loading tree with jQuery, Force.com, jsTree and REST JavaScript toolkit




I recently took part in my first CloudSpokes challenge and was lucky enough to be the winner. The challenge was to use the Salesforce REST JavaScript toolkit to create a tree component that represented the parent/child relationship of an object to itself (think parent account/account relationship). Another requirement was that it would load “lazily”, only loading one level of children at a time. This would allow for a quick overall page load if there was a deep and complex tree that needed to be rendered. Here is a link to the challenge.  

Below is an overview of my entry and my thought process. You can download the code in its entirety here.

What you will need

I decided to use jsTree to render my tree in. I had never used it before but it looked to have all the things I needed in order to render a fully functional tree. The documentation and files can be downloaded here:


I also had to download the REST JavaScript tool kit. This is a pretty slick toolkit created by Pat Patterson that makes using the REST API via JavaScript a breeze. You get the toolkit here:


Once I got both of these libraries I zipped them up and loaded them to my dev org as a static resource that I would reference later in my component.

Add all the files into a zip and load it to Salesforce

Lastly I created a custom object to run it on. This was a simple custom object named Parent Object with a lookup relationship to itself.

Parent Object 


Define lookup to itself
Now on to the code…


I decided to create my tree as a custom component so I would be able to use it across any object that has a relationship with itself. To learn more about custom components check out the Visualforce developer’s guide.


The first step was to define my custom component and all the attributes that would need to be passed to it to make it dynamic. We will see these referenced later in the code.




 

 

 

 
 


Next, we need to include the libraries from the static resource we uploaded earlier.




    

Then we define the div that the tree will be rendered in once the page loads.


Now we can get to the actual code that queries Salesforce and renders the parent/child tree. The following code is all written in JavaScript and I am leaving out the script tag in my demo because Blogger does not like it. The first thing I did was convert the attributes to JavaScript variables. This is not a necessary step. I just find it easier when I do not have to type the merge syntax around my variables throughout my code.


/////////////////////////////////////////////////
        //
        // VARS
        //
        //////////////////////////////////////////////////
         
      var objectToQuery = '{!objectToQuery}';
        var parentId = '{!parentId}';  
        var fieldToQuery = '{!childField}';
        var childRelationshipName = "{!childRelationshipName}";
        var parentRecordName = "{!parentRecordName}";

Since jsTree uses jQuery we need to get a reference to jQuery that works.  I also needed to load the jsTree themes and get an instance of the REST API Client.


//////////////////////////////////////////////////
        //
        // Get a reference to jQuery that we can work with
        //
        //////////////////////////////////////////////////
        
        $j = jQuery.noConflict();
        
        ///////////////////////////////////////
        //
        // Load the themes
        //
        ///////////////////////////////////////
        
        $j.jstree._themes = "{!URLFOR($Resource.restJavascript, '/rest/themes/')}";
        
        ///////////////////////////////////////
        //
        // Get an instance of the REST API client
        //
        ///////////////////////////////////////
        
     var client = new forcetk.Client('{!$Api.Session_ID}');

Next I created an instance of a tree and stick it in the “tree” div we created earlier.  You will see that three major things are defined here. (1) The plugins needed. (2) The parent node name, state and id. Notice that I use the id of the parent object as the id. I will reference that later as I build nodes for the tree. (3) The themes for the different nodes. These themes are what define what icons will show up for the different types of nodes and what type of children they can have.


//////////////////////////////////////////////////
        //
        // Instantiate the inital jsTree
        //
        /////////////////////////////////////////////////
        
        $j("#tree").jstree({ 
          "plugins" : [ "themes", "json_data", "ui", "crrm", "dnd",  "types" ],
        
             "json_data" : {
                 "data" : [
                     { 
                         "data" : parentRecordName,
                         "state": "closed", 
                         "attr" : { "id" : parentId }                     
                     },
                 ]
             },
             "types" : {
     "max_depth" : -2,
     "max_children" : -2,
     "type_attr" : "rel",
     "valid_children" : [ "default" ],
            
              ////////////////////////////////
              // Define the themes
              ////////////////////////////////
              
              "types" : {
    
       "children" : {
        "icon" : { 
         "image" : "{!URLFOR($Resource.restJavascript, '/rest/folder.jpg')}" 
        },
        "valid_children" : ["noChildren","children" ]
       },
       "noChildren" : {
        "icon" : { 
         "image" : "{!URLFOR($Resource.restJavascript, '/rest/leaf.jpg')}" 
        },
        "valid_children" : ["noChildren","children" ]
       },
       
       "default" : {
        "icon" : { 
         "image" : "{!URLFOR($Resource.restJavascript, '/rest/home.jpg')}" 
        },
        "valid_children" : [ "noChildren","children" ]
       }
     }
    } 
 });
         

The next two functions are the ones that query Salesforce for data. “firstNodeLoad” is called on page load and loads the children of the parent object. We will see this called later in my code. The second one called “loadNode” is used for every subsequent call and accepts an Id of a Salesforce object and queries for that object’s children.


//////////////////////////////////////////////////
    //
    // Load nodes
    //
    /////////////////////////////////////////////////    

 function firstNodeLoad() {
     
     var query = "SELECT Id," +fieldToQuery +", Name, (Select Id From "+childRelationshipName+") FROM " + objectToQuery + " WHERE " +fieldToQuery +" = '" + parentId + "'";
     client.query(query,parseNode);
     
    }
    
 function loadNode(parentIdPassed) {
    
     var query = "SELECT Id," +fieldToQuery +", Name, (Select Id From "+childRelationshipName+") FROM " + objectToQuery + " WHERE " +fieldToQuery +" = '" + parentIdPassed + "'";
      client.query(query,parseNode);
    
    }
    
    
Both of those functions call “parseNode” when their API call is complete and pass their results into it. This function parses the response and builds a node for each child found for that parent. It also determines if those children have children. 

//////////////////////////////////////////////////
    //
    // Parse the REST repsonse
    //
    /////////////////////////////////////////////////
    
    function parseNode(response) {
       
        for(var i=0; i < response.records.length; i++) {
         
         var hasChildren = false;
       
/// Determine if they have children
if(response.records[i][childRelationshipName]!= null) {
          
               hasChildren = true;
          }
   
   addNode(response.records[i][fieldToQuery],response.records[i].Name,response.records[i].Id,hasChildren);
  }
         
    } 
        
The final function “addNode” is called as each child is parsed in the parseNode function. This function takes all the information about that child and creates a node or “leaf” on the tree for the child. It defines things like style, id, closed, etc. You will notice that I use the record id for the id of the leaf. This way I will be able to quickly add children to it when I need to.
//////////////////////////////////////////////////
    //
    // Add each node
    //
    /////////////////////////////////////////////////   
    
    function addNode(localParentId,nodeName,nodeId,hasChildren) {
      
      ////////////////////////////////////////////
      //
      // Create a new node on the tree
      //
      ////////////////////////////////////////////
      
      if(hasChildren) {
   
    /// If it has children we load the node a little differently
    /// give it a different image, theme and make it clickable
    
    $j("#tree").jstree("create", $j("#"+localParentId), "inside",  { "data" : nodeName ,"state" : "closed","attr" : { "id" : nodeId,"rel":"children"}},null, true);

    
   } else {
   
    // Nodes with no trees cannot be opened and 
    // have a different image
      
       $j("#tree").jstree("create", $j("#"+localParentId), "inside",  { "data" : nodeName ,"state" : "leaf","attr" : { "id" : nodeId,"rel":"noChildren"}},null, true);
   
   }
   
 }
 
Lastly I need to bind some events to the nodes that are created in the tree.

The first event was to load any child nodes of a leaf if it was opened up by the user. This was a major requirement of the challenge. Up until now in our code we only query one level of the tree. I accomplished this by using the “open_node.jstree” method that is part of jsTree. Basically every time a node is opened I call the “loadNode” method on the children of that node and load up any children they may have.
//////////////////////////////////////////////////
    //
    // Add the logic to query if there are children
    //
    /////////////////////////////////////////////////
 
 $j("#tree").bind("open_node.jstree", function (e,data) {
  
  /// Make sure it is not the parent node 
  
  if(data.args[0].attr("id") != parentId) { 
  
   // Get the current node
           
           var thisNode = $j('#'+data.args[0].attr("id"));
          
           /// This makes sure that it does
           /// not load the childeren more than once
           
           if(thisNode.find('li').length == 0) {
           
            /// Load all the child nodes
            
             loadNode(data.args[0].attr("id"));
           
            }
  }   
    })
Then I also delegated the click of links (record names) within the tree to open up the record they represented in a new window.

//////////////////////////////////////////////////
    //
    // Setting up the link to the object on the label 
    // of a tree node
    //
    //////////////////////////////////////////////////

   $j("#tree").delegate("a","click", function (e) {
    
    /// Dont open a window for ultimate parent
    if($j(this).parent().attr("id") != parentId) {
    
     window.open("/" + $j(this).parent().attr("id"),"mywindow","status=1,toolbar=1,location=1,menubar=1,resizable=1,scrollbars=1");

    }
  });
 



Finally when it is all done I call the “firstNodeLoad” method to kick it all off.
////////////////////////////////////////////////
 //
 // Load the first node on window load
 //
 ////////////////////////////////////////////////
 
 window.onload = firstNodeLoad;


All I had to do now was include this component in a Visualforce page. You will see that we call the "lazyLoadingTree" component and then pass into it the attributes required to render the tree. This component could be called on any object that had a child/parent relationship with itself.



 









Then include that page in a standard page layout and I get this pretty little tree that loads “lazily”.




Hopefully this was helpful. It was my first time working with jsTree and I am a bit of a jQuery hack. As always would love to hear any feedback/suggestions. Thanks again to Pat Patterson for putting the toolkit together as well as CloudSpokes for putting on some fun challenges!



You can download the code in its entirety here.


Tuesday, November 11, 2008

Creating a Visualforce custom component with jQuery

Recently at the latest Salesforce.com Dreamforce event I attended a session on creating custom components in Visualforce. The best way to think about custom components is to think of them as reusable "widgets" that can be used in any Visualforce page with mininal duplicate coding. I recently also discovered jQuery...a very powerful javascript library. One of my "homework" assignments I assigned myself at Dreamforce was to create some custom components that leverage the jQuery library.

Why use jQuery to create a Visualforce component?

jQuery is a fast and concise cross-browser JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. To learn more about the power of jQuery visit their site at: http://jquery.com/

Combining jQuery with Salesforce.com allows us to leverage the proven UI components of the library with the power of Salesforce.com. The new Visualforce functionality in Salesforce.com allows us to create reusable components that we can insert in any other Visualforce page.

In the following example we will walk through getting jQuery, creating a static resource, creating a custom component, and finally adding that custom component to a visual force page.

Steps 1: Get jQuery

jQuery is a lightweight javascript library that comes with a bunch of very stable UI elements (Dialog boxes, accordion widget, spinners, etc).

Get the latest build of jQuery from the jQuery website. http://jquery.com/

Steps 2: Zip it up

Once you have downloaded the latest build of the jQuery library .zip it up for upload to Salesforce.com. That is right...we will be able to reference all the files in the .zip file from within the custom component we will create. No need to upload all the files individually.



Steps 3: Create a static resource

Next we will log into Salesforce.com and create a static resource calling it "JQuery_UI". Upload the jQuery .zip file to this resource and save it for reference in your custom component.



Steps 4: Create the custom component

Next we will write the code for the reusable Visualforce custom component. Remember that the value of creating a custom component is that you only have to create the code once and you will be able to reuse the component with one line of code in any Visualforce page. Also the static resource will only be loaded once per page no matter how many times you call the custom component in a Visualforce page (reducing page load times).

Below is the simple code for our custom component. It could be extended to accept and pass variables however, our component will be a stand-alone component that simply displays a nicely formatted jQuery dialog box with a message in it for the user.


Items to note in the code:

Lines 5-7: You will see in the code that we simply reference the files in the static resource we created.
Lines 12-24: Creating the architecture of the dialog box

Lines 30-34: Invoking the jQuery library to make the dialog box appear.


Steps 5: Include custom component in a Visualforce page

The last step is to include the custom component on any Visualforce page. As we see below it as easy as adding one tag.



Step 6: Call your Visualforce page

So now we call the Visualfoce page and we see our "mashup" of jQuery and Salesforce.com Visualforce code.


For more information see:
Salesforce.com - http://wiki.apexdevnet.com/index.php/Apex_and_Visualforce
jQuery -http://jquery.com/