+ Start a Discussion
sparkysparky 

calling apex webservice: ajax vs flex

It's super easy to call an apex webservice method from the ajax toolkit, since it very helpfully provides the sforce.apex.execute method.

I assumed there would be something equivalent in the flex toolkit, but I can't find anything, and then stumbled across this thread from a few months ago:
http://community.salesforce.com/sforce/board/message?board.id=apex&thread.id=518

Is this still the state of affairs?  If so, sounds like not only is it not easy to call an apex method from flex, but no-one has yet figured out how to do it.  (or at least hasn't figured it out and then replied to the forums.)

Seems like that can't be right, but I can't find anything anywhere (forums, docs, object structure within flex builder, etc.) to suggest otherwise.

Is anyone calling apex webservice methods from Flex, and if so, how?

Thanks much!





Best Answer chosen by Admin (Salesforce Developers) 
Ron WildRon Wild
Since we're on the topic of calling Apex webservices from Flex ... I had to do a little research to figure out how to pass complex types to the webservice using the Flex toolkit.  The answer is not obvious, so I thought I'd share:

Assuming you have an Apex webservice defined:

Code:
WebService static String test( MyClass.ShippingInfo ship) {
 return 'Shipping info is '+(ship==null—'':ship.toString());
}

 and the subclass definition you are using for the 'ship' parameter:

Code:
// Address object used to pass shipping information to PayPalConnector methods
global class ShippingInfo {
  webservice String firstname;
  webservice String lastname;
  webservice String company;
  webservice String address;
  webservice String city;
  webservice String state;
  webservice String country;
  webservice String postalCode;
  webservice String phone;
} 

 Then the call from Flex will be in the form of:

Code:
 // Shipping Info
 var b:Object = {firstname:'Joe',
   lastname:'Test',
   address:'123 Chestnut',
   city:'Anytown',
   state:'CA',
   postalCode:'12345',
   country:'US',
phone:'760-555-1212' }; var shippingInfo:ObjectProxy = new ObjectProxy(b); var p_ship:Parameter = new Parameter("shippingInfo", shippingInfo,true); var params:Array = [ p_ship ]; // Make call to Apex method apex.execute('MyClass','test', params, new AsyncResponder(function(result:Object):void {Alert.show(ObjectUtil.toString(result));}, function(result:Object):void {Alert.show(ObjectUtil.toString(result));} ), 'mynamespace');

ObjectProxy was the missing link.  The Flex for Apex toolkit checks for this type as it parses the array of Parameter objects, and converts the object to XML as it's building the outbound SOAP message.   Also, I believe Apex only supports subclassing one level deep.

Hope that saves everyone else a little bit of time.

Cheers,



All Answers

Ron HessRon Hess
yes, this is now working.
The changes to enable this went out with a recent build.

In the latest build, look for the directory test/FlexUnit/flex2/ExecuteTestCase.as

There you will see examples of the call to execute() from a flex app.

I can understand how the published doc is out of date, i'll try to update this soon.

best of luck!
sparkysparky
Thanks, Ron!

I found this example, and am attempting to emulate it, but something isn't working.  Maybe there's something I'm not grokking about the syntax?

My webservice method has a declaration that looks like this:

Code:
global class NCI_RoomAssignment {

 Webservice static integer AssignRegistrationToRoom ( string RegId, string ReservationId, string Room) {
 
  integer Result;

                (snip...)
  
  thisReg.Reservation__c = TargetRes.id;
  thisReg.Room__c = TargetRes.Room__c;
  update thisReg;
  
  Result = 1;
    
  return Result; 
 }  
}

 I've written an apex test for this, and it passes.  So seems to be working.

I'm attempting to call this from Flex w/ something that looks like this:

Code:
 private function TempAssignRegToRes ( RegId:String, ResId:String, RoomName:String ):void {
  
   var RegParam:Parameter = new Parameter ("RegId",RegId);
   var ResParam:Parameter = new Parameter ("ReservationId",ResId);
   var RoomParam:Parameter = new Parameter ("Room",RoomName );
   
   var params:Array = [RegParam,ResParam,RoomParam];
   
   var tempCallback:AsyncResponder = new AsyncResponder (
    function(result:Object):void {
     trace ('responder running!');
    }
   );
   
   trace('executing...');
   apex.execute("NCI_RoomAssignment",
    "AssignRegistrationToRoom",
    params,tempCallback);
   trace ('executed');
 
  }

 and I'm calling this from a button w/
Code:
click="TempAssignRegToRes('a0A60000001xxxxxxx','a0D60000000xxxxxxx','Room 1');"

 When I click the button, I see "Executing..." and "Executed" in the console, but never "responder running", and the desired result does not occur.

What am I doing wrong in calling this webservice method from Flex?

Thanks very much!







Message Edited by sparky on 01-02-2008 05:40 AM

Message Edited by sparky on 01-02-2008 11:36 AM
DevAngelDevAngel
You should create a fault callback function in your responder.  Likely some fault is occuring and since you didn't create a fault callback, you don't know about it.
sparkysparky
Thanks, Dave.  Good advice, as usual, and when I did that I figured out the problem.  I had forgotten that I needed to set permissions on the apex class itself, because I had forgotten that I was using a non-sysadmin user profile to test the Flex app.

Working now.  And let this be a lesson to all you readers at home.
Ron WildRon Wild
Since we're on the topic of calling Apex webservices from Flex ... I had to do a little research to figure out how to pass complex types to the webservice using the Flex toolkit.  The answer is not obvious, so I thought I'd share:

Assuming you have an Apex webservice defined:

Code:
WebService static String test( MyClass.ShippingInfo ship) {
 return 'Shipping info is '+(ship==null—'':ship.toString());
}

 and the subclass definition you are using for the 'ship' parameter:

Code:
// Address object used to pass shipping information to PayPalConnector methods
global class ShippingInfo {
  webservice String firstname;
  webservice String lastname;
  webservice String company;
  webservice String address;
  webservice String city;
  webservice String state;
  webservice String country;
  webservice String postalCode;
  webservice String phone;
} 

 Then the call from Flex will be in the form of:

Code:
 // Shipping Info
 var b:Object = {firstname:'Joe',
   lastname:'Test',
   address:'123 Chestnut',
   city:'Anytown',
   state:'CA',
   postalCode:'12345',
   country:'US',
phone:'760-555-1212' }; var shippingInfo:ObjectProxy = new ObjectProxy(b); var p_ship:Parameter = new Parameter("shippingInfo", shippingInfo,true); var params:Array = [ p_ship ]; // Make call to Apex method apex.execute('MyClass','test', params, new AsyncResponder(function(result:Object):void {Alert.show(ObjectUtil.toString(result));}, function(result:Object):void {Alert.show(ObjectUtil.toString(result));} ), 'mynamespace');

ObjectProxy was the missing link.  The Flex for Apex toolkit checks for this type as it parses the array of Parameter objects, and converts the object to XML as it's building the outbound SOAP message.   Also, I believe Apex only supports subclassing one level deep.

Hope that saves everyone else a little bit of time.

Cheers,



This was selected as the best answer
shanumanshanuman
Hi Ron,

I created a sample apex web service class and I generated a wsdl document by clicking the button generate wsdl.

In my flex application, I have a login page, I have logged successfully through default web service url.

after successful login, now I'm trying to call the exposed wsdl in my flex application.

I'm not understanding, how and where I have to call this exposed wsdl in my flex application.

Please help me out on this.

-sunil 
Ron WildRon Wild
In another development environment you might use the wsdl file to generate 'wrapper' classes you could then call to access your web services (e.g. in .net, java ...) but with the Flex dev environment I don't think you have that option, so the wsdl file is not required.  Just model your application after the code sample above and change the name of the method (include the namespace, if applicable) and pass in parameters as shown.

Regards,



shanumanshanuman
Hi Ron,

Thanks for the response,

In our flex application, we are entering the username and password + security token in the login page for connecting the salesforce, its working fine.

Suppose if portal user want to use the same flex application to access the custom portal.  Portal users have the username and password but in the password field users has to enter password + security token.

But in the custom Portal application they don't have an option like setup > My Personal Information > Reset My Security Token.

So my question is how to generate security token for the portal users?

Please let me know any suggestions for this.

Thanks in advance.

--sunil


Message Edited by shanuman on 01-04-2009 11:36 PM
myztakenmyztaken

Ron...

 

I tried to use your example an I'm getting a weird error:

 

 

System.StringException: Invalid id: Class.optimizer.OptimizerWS: line -1, column -1

 

This is what I'm using after I read your post:

 

Apex WS:

 

 

global class OptimizerWS { global class PowerChart { webservice String localId; webservice String oppId; webservice String sfId; webservice List<PowerBox> powerBoxes; } global class PowerBox { webservice String localId; webservice String text1; webservice String text2; webservice String text3; webservice String text4; webservice String value1; webservice String value2; webservice String value3; webservice String value4; webservice String x; webservice String y; webservice String sfId; webservice String powerChartId; } webservice static List<String> savePowerChart(OptimizerWS.PowerChart powerChart){ System.debug('Entering loadPowerChart: Chart=>'+powerChart);//+' oppId=>'+oppId); List<String> errors = new List<String>(); System.debug(powerChart); return errors; } }

 

 Flex:

 

 

public function savePowerChart(oppId:String, callback:AsyncResponder, powerChart:PowerChart):void { savePowerChartCallback.context.clientCallback = callback; var powerchartProxy:ObjectProxy = new ObjectProxy(powerChart); var executeParams:Object = { className: "OptimizerWS", methodName: "loadPowerChart", args: [new Parameter("powerChart",powerchartProxy,true)], callback: savePowerChartCallback, nameSpace: "optimizer" }; session.execute(executeParams); }

 Powerchart/Powerbox Flex Classes:

 

 

public class PowerChart { public var localId:String; public var sfId:String; public var oppId:String; public var powerBoxes:Array; }

 

public class PowerBox { public var localId:String; public var text1:String; public var text2:String; public var text3:String; public var text4:String; public var value1:String; public var value2:String; public var value3:String; public var value4:String; public var x:Number; public var y:Number; public var sfId:String; public var powerChartId:String; }

 

I would really appreciate if you can help me out with this... I've already wasted 2 days trying to send a Object back from Flex to Salesforce, and this approach also works for me, but I can't get it to work...

 

Thanks in advance...

~ foe