+ Start a Discussion
XactAndyXactAndy 

Tree based summary

I would like our users to be able some objects and relationships in an
expandable  tree based form. Can anyone suggest the best way to do this, e.g.
S-Control, VisualForce, Ajax?

Thanks,

Andy
jf317820jf317820
flex has a pretty good tree control
eyewellseeyewellse
It took me a while to figure this out, so I thought I would post this for the community, and show how we all can use trees in Flex. Here is my code:

In my case, I wanted to populated a tree of folders from Document table data, and the documents in them.


There is a binable file of type XML at the top of my file:
Code:
    [Bindable]
    private var myData:XML = <documents name='Documents Tab'>
                            </documents>;

 

The function getFolderList below can be called upon the event you choose.

    Code:
private function getFolderList():void
    {

       //query all the files in the documents tab that fit the criteria, including their folder information
        apex.query("SELECT Id, Name, Type, Description, Folder.Name, Folder.Type FROM Document ... ORDER BY Folder.Name",
            new AsyncResponder(function (qr:QueryResult):void
              {   
                 
                  // create a variable to store the name of the folder currently being processed
                var sCurFolder:String = "";
             
                // loop through all files found in query
                  for (var j:int=0; j<qr.records.length; j++) {
                     

                    if (qr.records[j].Folder.Name != sCurFolder) {
                      // 3 if the folder has not been seen before in the ordered set of folders, then create the folder node            
   
                         if ( j != 0)
                         {
                             // (doesn't run the first time through the loop) if a new folder name is discovered,
                             // and this isn't the first time through
                             // save the last folder to the tree
                             myData = myData.appendChild(xFolder);
                         }
                        
                         // Create a new folder node
                         var sFolderXMLString:String = "<folder id='" + j +"' name='" + qr.records[j].Folder.Name + "'></folder>";
                         var xFolder:XML =new XML(sFolderXMLString);
                        
                         // set the name for the current folder name
                         sCurFolder = qr.records[j].Folder.Name;
                        
                     } // end if
                     
                     // add file nodes
                     var sFileXMLString:String = "<file id='" + qr.records[j].Id +"' name='" + qr.records[j].Name + "'/>";                                   
                     var xFile:XML= new XML (sFileXMLString);
   
                     // append the file node to the folder node
                     xFolder.appendChild(xFile);
                    
               } // end for loop, now that all rows in the result have been processed.
              
               // append the filled folder node to the tree
               myData = myData.appendChild(xFolder); // append last node
                 
              } )
        );
       
    }

 





.... Good Luck!


Erik
mschmidtmschmidt
I've adapted Eric's code to provide a real tree view of the documents and folders.

Here is my code:
Code:
<—xml version="1.0" encoding="utf-8"–>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
 xmlns:salesforce="com.salesforce.*"
 applicationComplete="login();">
 
<salesforce:Connection id="apex" /> 
 
<mx:Script>
 <![CDATA[
  import com.salesforce.AsyncResponder;
  import com.salesforce.objects.LoginRequest;
  import com.salesforce.results.QueryResult;
  
 [Bindable]
    private var myData:XML = <documents name='Document Tree'>
                             </documents>;
                             
 private function login():void {
   apex.login(  new LoginRequest({
         server_url : this.parameters.server_url,
         session_id : this.parameters.session_id,
         // put your own info here to test standalone
         username : 'myname@mycompany.com',
         password : 'PASSWDsecuritytoken',
         callback : new AsyncResponder(getFolderList)
         })
   );
 }
  
 private function getFolderList(result:Object):void
    {
     apex.query("SELECT Type, Name, Folder.Name FROM Document ORDER BY Folder.Name, Name",
            new AsyncResponder(
            function (qr:QueryResult):void
              {   
               var xFileString:String = "";
               var folderName:String = "";
               var xFolder:XML;
               
                // loop through all files found in query
             for (var j:int=0; j<qr.records.length; j++) 
             {
              // check for new folder
              if (folderName != qr.records[j].Folder.Name)
              {
                 // if it's not the first loop
                 if (j != 0)
                 {
                  //add folder to the tree
                  myData = myData.appendChild(xFolder);
                 }
                 folderName = qr.records[j].Folder.Name;
                 xFolder = new XML("<folder label='" + folderName + "'></folder>");
                }
                // add file to folder
                 xFileString = "<document label='" + qr.records[j].Name + "' type='" + qr.records[j].Type + "'/>";
                 xFolder = xFolder.appendChild(new XML(xFileString));
             }
             // add the last folder!
             myData = myData.appendChild(xFolder);
                         
             // bind the data to the tree and textArea control!!
          myTree.dataProvider = myData;
          txtXML.text = myData;
              } ,
            function (fault:Object):void {}
            )
        );
    }
    
    [Bindable]
            public var selectedNode:XML;
    
    // Event handler for the Tree control change event.
    public function treeChanged(event:Event):void {
        selectedNode=Tree(event.target).selectedItem as XML;
    }
 ]]>
</mx:Script> 
 
 <mx:Panel title="Document Folder Tree" height="535" width="687" 
        paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" x="141" y="44">

        <mx:Label width="100%" color="blue" 
            text="Select a folder in the Tree control."/>

        <mx:HDividedBox width="100%" height="100%">
            <mx:Tree id="myTree" width="60%" height="100%" labelField="@label"
                showRoot="false" change="treeChanged(event)"/>
            <mx:TextArea text="loading data ..." id="txtXML" width="60%" height="100%"/>
        </mx:HDividedBox>     
        
        <mx:TextArea height="68" width="100%"
                text="Selected Item: {selectedNode.@label}, Type: {selectedNode.@type}"/>
        
    </mx:Panel>
</mx:Application>

Cheers!
mschmidt
HalcyonandonHalcyonandon
Hey,

I know this was posted awhile back, but for those of you (like myself) that are using visualforce and apex and would like to create a tree structure using custom objects for the data AND do not wish to use flex, here is the solution I reached thanks to the great help provided by Ron Hess, whom I'm sure you will see helping others throughout these forums.

Now that that run on sentence is out of the way, on to the solutions:

For my data structure, I created 4 custom objects related as follows...

Application(Applications)

    Application Name
    ApplicationID (auto number)
    
Grouping(Groupings)
Lookup Field to Application
    
Grouping Name
   GroupingID (auto number)

Category (Categories)
Lookup Field to Grouping
    
Category Name
    CategoryID (auto number)

Topic(Topics)
Lookup Field to Category
    
Category Name
   TopicID (auto number)
   Directions (longtext area(32,000))

In visualforce I created a custom controller to handle these objects.  Ron was able to show me the right way to write this controller and I was very pleased with the simplicity of it.  The main thing to look at is the way the custom objects are handled in relation to each other, building the container object's list, grouping.

Code:
public class tree {

List<grouping__c> grouping;

public class Groups {
Grouping__c g;
List<Category__c> cats;
public Groups(Grouping__c gin) { g = gin;
cats = [Select c.Name, c.Grouping__r.GroupingID__c, c.Grouping__r.Name,
c.Grouping__c, c.Description__c, (Select Name, Directions__c,
Priority__c From Topics__r) From Category__c c
where c.Grouping__c = :gin.id];
system.debug(cats);
}
public List<Category__c> getCategories() { return cats; }
public string getName() { return g.name; }
}

public List<Groups> getgrouping() {
if(grouping == null)
grouping = [select name,id from Grouping__c
where application__r.name = 'cod'];
List<Groups> ret = new List<Groups>{};
for ( Grouping__c g:grouping) {
ret.add( new Groups( g ) );
}
return ret;
}


}

 
Then in the visualforce page, the apex code to display this data in a tree structure goes as follows:

Code:
<ul class="tree" style="margin-top: -1ex;">
<apex:repeat value="{!grouping}" var="group">
<li class="treeHeader">{!group.name}</li>
<apex:repeat value="{!group.categories}" var="cat">
<li class="closed"> <a href="#">{!cat.name}</a>
<ul><apex:repeat value="{!cat.topics__r}" var="topic__c">
<li><a href="#">{!topic__c.name}</a></li>
</apex:repeat>
</ul>
</li>
</apex:repeat>

</apex:repeat>
</ul>

 
I used CSS and javascript to create the tree.  I'm assuming you'll know how to build this, if not you can find source for all different kinds of tree structures via Google search.  I prefer the unordered list based tree structure over something like YUI because its much easier to use in connection with apex (for me anyways).

The end result is a very pleasing interface for our help system with a left nav tree menu that is comparable to the salesforce help system's tree structure.  Using custom objects allows anyone in our company to add topics to our help system through salesforce with ease.