Pages

Thursday, December 31, 2009

Flex Rich Clients and Complex Logics!

Nowadays many developers are driven or forced toward using richer UIs such as Adobe Flex by their customers. Using Adobe Flex and AIR technologies you can create fancy user interfaces with drag and drop functionality, applications that easily integrates with force.com Webservice APIs, takes the data offline and syncs it back to the source once connected to internet again.

All that is very interesting and cool, however one should bear in mind that all the operations, Web Service calls, etc are running in the user's browser (client machine) not on force.com servers.

For this very specific reason, one has to consider how to best design the applications to utilize the cloud computing power of force.com technology and leave the chunk of heavy operations (program logic) out of the client's machine.

The following example which is proven to be useless in the real world, shows you how to host your main operations (chunk of code) in the force.com platform and then share them in form of Web Service methods with your flex applications.

I first start with creating a new Apex class which is pretty much the same as any other Apex class you have seen before but with a few minor changes.

The Apex class is where I intend to do my main operation. I'd rather use my Flex application to utilize such Apex classes and merely view the data in proper form to the user (where possible).

Points:
  • Any Apex class which wants to share a method over the web needs to be marked as "global"
  • Any method of this class that must be reachable by our Flex app should be mark as "WebService"
  • Any internal variable or class that are used in the parameter or as output of the WebService methods also needs to be marked as "WebService".
Below is the implementation of a sample Apex class which shares a method as "WebService".



global class MyWebService
{
global class CompositeData
{
WebService String CompanyName;
WebService String FullName;
WebService String Id;
}

Webservice static MyWebService.CompositeData[] SearchContacts(String keywords)
{
List<CompositeData> results = new List<CompositeData>();

//search logic.
List<List<SObject>> data = [Find :keywords IN ALL FIELDS
RETURNING Contact (id, Name, Account.Name)];

List<SObject> contacts = data[0];

for(SObject contact : contacts)
{
CompositeData cd = new CompositeData();

cd.Id = contact.Id;
cd.FullName = ((Contact)contact).Name;
cd.CompanyName = ((Contact)contact).Account.Name;


results.add(cd);
}

return results;
}
}



The above example demonstrates how you can share a such class as structure other than Apex's SObject class.

This allows you to run multiple queries and aggregate data from various objects in force.com platform and then return them back to the Flex application using the new structure ("CompositeData" in this example).

Now let's see how we can use the above method in our Flex application. Let's say that we use a Flex wrapper class to call this Apex method and then expose this class (Flex class) to our Flex application.

I believe the comments in the code should guide you through the steps.



package com
{
import com.salesforce.AsyncResponder;
import com.salesforce.Connection;
import com.salesforce.objects.Parameter;
import com.salesforce.results.Fault;

import mx.collections.ArrayCollection;
import mx.controls.Alert;



public class MyFlexWebServiceClient
{

//a public property to provide access to the final results
public var results : ArrayCollection;

//force.com connection object
private var binding : Connection;

//the class constructor
public function MyFlexWebServiceClient()
{
results = new ArrayCollection();
}

// this method will be called from the Flex application script
// section once it is instantiated
public function init(Connection sfdcConnection)
{
//the class receive the SFDC connection object from the parent application
this.binding = sfdcConnection;

if (!this.binding.IsLoggedIn)
throw new Error("Connection to the server is not available.");
}

//this method is the wrapper method that calls the force.com webservice
public function execute(keywords: String): void
{
//validation
if (keywords == null || keywords.length <= 0)
{
//handle the invalid data
return;
}

//preparing the parameters of the web service method
var params : Array = new Array(1);

var param1 : com.salesforce.objects.Parameter = new Parameter("keywords", keywords);
params[0] = param1;

// using SFDC Aysync Responder to get the results in flex
var tempCallBack: AsyncResponder = new AsyncResponder(
function(result:Object):void {

if (result != null)
this.results = result as ArrayCollection;
else
Alert.show("Result is empty.", "Info");
},
function(result: Fault):void { Alert.show("Operation failed", "Error"); }
);

// call the execute method of the SFDC connection object to reach out the web service
binding.execute("MyWebService", "SearchContacts", params, tempCallBack);

}

}
}



Happy holidays and new year!