Working with yFiles FLEX and BlazeDS
Tips & TricksSummary
Description
Usually, a yFiles FLEX client communicates with a yFiles FLEX server component by using class RoundtripHandler (client)/GraphRoundtripSupport (server) to transfer the graph and any additional data between client and server. These support classes take care of automatic (de)serialization of the graph instance.
However, in a BlazeDS application, client-server communication is usually handled using RemoteObjects that allow to invoke server-side functionality directly from the client. When parameters are passed to the remote methods, or when the method has a return value, these values are serialized using the AMF format. Because the default yFiles FLEX IGraph implementation cannot be serialized automatically using the AMF format, the yFiles FLEX API provides a ExternalizableGraph implementation, that can be used as a parameter/return value for remote object calls instead.
Example: Layout Calculation
The following example code demonstrates how to perform an automatic layout calculation on the BlazeDS/yFiles FLEX server for a graph displayed on the client.
Calculating a Layout: Server
The following code snippet shows how to perform a layout calculation on the server using class ExternalizableGraph
public class TestService {
public ExternalizableGraph layout( ExternalizableGraph inputGraph ) {
// retrieve the deserialized LayoutGraph
LayoutGraph graph = inputGraph.getGraph();
// calculate a layout
new BufferedLayouter( new IncrementalHierarchicLayouter() ).doLayout( graph );
// wrap the modified graph in an ExternalizableGraph for automatic serializaiton
ExternalizableGraph outputGraph = new ExternalizableGraph();
outputGraph.setGraph( graph );
// and return the graph to the client
return outputGraph;
}
}
Calculating a Layout: Client
On the client, we need a RemoteObject that calls the methods provided by the server-side Class:
<mx:RemoteObject id="remoteService"
destination="flextest"
result="handleResult(event);"
fault="handleFault(event);"/>
Here, the "destination" parameter corresponds to the destination defined in the BlazeDS remoting-config.xml.
To send the request to the server, we can just use the name of the server-side method:
private function sendRequest( evt:Event=null ):void {
// wrap the graph in a ExternalizableGraph for serialization ..
var extGraph:ExternalizableGraph = new ExternalizableGraph();
extGraph.graph = graphCanvas.graph;
// .. and pass it to the remote method
remoteService.layout( extGraph );
}
In the handleResult() event handler, we update the layout of the graph that is shown in the client-side GraphCanvasComponent:
// a layout result was received: update the graph shown in the canvas
private function handleResult( evt:ResultEvent ):void {
// unwrap the graph..
var result:ExternalizableGraph = evt.result as ExternalizableGraph;
if( null != result ) {
var resultGraph:IGraph = result.graph;
// .. and morph the graph to the new layout
graphCanvas.morphGraph( resultGraph );
}
}
Adding Custom Data
Data that is associated with graph elements through mappers will also be (de)serialized automatically using the AMF format, if no custom serializer/deserializer has been registered for the data type. Assume there is a simple bean class like this:
Custom Data: Server
package demo;
public class CustomData {
private String myProperty = null;
public String getCustomProperty() {
return myProperty;
}
public void setCustomProperty(String myProperty) {
this.myProperty = myProperty;
}
}
Custom Data: Client
package demo
{
public class CustomData
{
private var _myProperty:String = "DefaultString";
public function get customProperty():String {
return this._myProperty;
}
public function set customProperty( value:String ):void {
this._myProperty = value;
}
}
}
Since this class is available both on the client and server, has only a no-arg constructor and simple getter/setter properties, it can be automatically (de)serialized by the Flex/BlazeDS framework using the AMF format.
Add the custom data on the client:
// Create a DictionaryMapper that is configured for automatic serialization
var mymapper:IMapper = new DictionaryMapper( false, "mytag", GraphMLConstants.SCOPE_NODE,
GraphMLConstants.ATTR_TYPE_COMPLEX, true );
// register the mapper with the graph's mapper registry
graph.mapperRegistry.addMapper( "mytag", mymapper );
// map instances of the custom data class to some nodes
mymapper.mapValue( node1, new CustomData() );
mymapper.mapValue( node2, new CustomData() )
Retrieve and modify the custom data on the server:
// add these lines to the layout method defined above:
// retrieve the DataProvider
DataProvider myDP = graph.getDataProvider("mytag");
for( NodeCursor nc = graph.nodes(); nc.ok(); nc.next() ) {
CustomData thing = (CustomData) myDP.get(nc.node());
thing.setCustomProperty( "server modified property");
}
The modified data will now be available in the client's handleResult() function:
var mapper:IMapper = resultGraph.mapperRegistry.getMapper( "mytag" );
for each( var node:INode in resultGraph.nodes ) {
if( null != mapper ) {
trace("tag value: "+CustomData( mapper.lookupValue( node ) ).customProperty);
}
}