Welcome!

Adobe Flex Authors: Yakov Fain, Keith Swenson, Jacques Durand, Pat Romanski, Liz McMillan

Related Topics: Adobe Flex

Adobe Flex: Article

Flash, Web Services, and Data Binding

Part 2 - Using data binding through code

In Part One (MXDJ, vol. 3 issue 2) we looked at one of three different ways to consume a Web service, here we look at two other ways and some of the pros and cons of each approach.

In this next example we'll explore using data binding in a different way from Pt. 1, by not relying on the Bindings tab of the Component Inspector panel. Instead, we'll create the bindings through ActionScript.

Example 2 - Creating Bindings Through ActionScript
To work through this example, we'll build the interface the exact same way as the previous example, leaving out the "add binding" steps. Either rebuild the interface following the previous instructions or take your complete Example 1 from Pt. 1 and simply remove the bindings on the components (by clicking the minus icon in the Binding tab).

Before we get to the ActionScript that needs to be written on the actions layer, first we'll examine the two classes that make data binding possible. Flash MX 2004 Professional includes the mx.data.binding.Binding class that does all of the heavy lifting. It uses a helper class mx.data.binding.EndPoint that is used to define the source and destination for the binding bridge.

The Binding constructor takes a source EndPoint that supplies the property to be bound, and a destination EndPoint that determines where that property is transferred to. Optionally, we can assign a formatter to transform the property from the source before it arrives at the destination. We can also optionally specify the binding as being two-way. Formatters and two way bindings are left as exploration pieces - discovery is a great learning mechanism.

An EndPoint is an object consisting of three main properties: component, property, and event. The "component" property of an EndPoint instance is a reference to the component the binding applies to, and the "property" property is a string defining what property is being bound or bound to. The "event" property is usually only necessary for "source" EndPoints, and is typically set to either "change" or "result." The "event" property can either be a single event as a string, or an array of events if multiple events are required.

Creating a new Binding instance and passing it a source and destination EndPoint is enough to get binding to work. Use the following two code blocks on the "actions" layer of the movie to hook up the data binding through script.


import mx.data.binding.Binding;
import mx.data.binding.EndPoint;

// bind the msg_ta text area to
   the msg parameter of the web 
   service connector
var src = new EndPoint();
src.component = msg_ta;
src.property = "text";
src.event = "change"; // event is
   either a single event string,
   or an array of event strings

var dest = new EndPoint();
dest.component = msg2morse_wsc;
dest.property = "params";
dest.location = ["msg"]; // this binds
   to params.msg

// creating the binding is as simple
   as this:
new Binding(src, dest);

Alternatively, we can pass anonymous objects to the Binding constructor, bypassing the need for the EndPoint helper class. This makes for more compact code, although its readability can be argued to be either better or worse depending on your point of view.


// bind the result of the msg2morse_
   wsc to the text of the morse_ta
new Binding({component:msg2morse_wsc,
   property:"results", event:
   ["result"]},
   {component:morse_ta,
    property:"text"});

// bind morse_ta text to the msg
   parameter of the morse2msg_wsc
new Binding({component:morse_ta,
   property:"text", event:["change"]},
		{component:morse2msg_
   wsc, property:"params",
   location:["Morse"]});

// bind the result of the morse2msg_
   wsc to the text of the msg_ta
new Binding({component:morse2msg_wsc,
   property:"results",
   event:["result"]},
   {component:msg_ta,
    property:"text"});


// wire the buttons
msg2morse_btn.onRelease = function() {
	msg2morse_wsc.trigger();
}
morse2msg_btn.onRelease = function() {
	morse2msg_wsc.trigger();
}

In the first portion of the code, notice that we explicitly create an EndPoint for both the source and the destination. In the remaining code things are done a little different because using the EndPoint helper class is not a requirement. Rather, as long as we pass an object with the appropriate properties, the Binding class still functions correctly. With this in mind, we leverage anonymous objects to define the source and destination for the binding. Doing so makes the code more concise though it may or may not increase readability as that is a matter of personal opinion.

The only other interesting thing to note in the code is the additional use of the "location" property. If the "property" property of the component is complex (that is, not a simple value like 1 or "hello"), the "location" property points to the data field in the complex "property" object. In the example, the "param" property of the web service connector is an object and therefore complex, so we use "msg" as the "location" property in order for the Binding class to find the param.msg data field correctly.

Now, again, test the movie and see the magic. We've achieved the exact same results but instead of the "jungle code" approach of the Bindings tab, we've consolidated all of the binding code in one location. This makes the "magic" aspect a little bit more obvious, and should allow for easier updates in the future. Having a single location to look for certain information is a boon.

Like Example 1 in Pt 1, however, Example 2 is has a dark side as well. There's still a "magic" factor surrounding the web service connector supplied by Macromedia, and data binding adds a lot of unnecessary overhead behind the scenes. The former may not be that much of a concern, but if performance is paramount than the use of data binding needs to be reevaluated.

"But Darron, this article is about data binding, and now you're telling us not to use it?" Correct, sort of. Data binding has its place, but here's what you probably didn't know: every time you type a letter into the message area, a "change" event is generated because the "text" property has been changed; because of this, the glue that is data binding will update the parameter of the web service connector to be the text contained in the text field to keep them in sync.

While this doesn't sound like that big of a deal, consider the following scenario. Imagine typing the simple 4 letter word "test." In doing so, we've generated 4 change events, and set the "msg" parameter of the web service connector to be "t," "te," "tes," and finally "test." What a waste of time! In our case, we only care what the value of the text is when we invoke the web service because that is the only time the value is actually used. Instead of 4 calls, we really only need to make one.

Imagine, now, typing the above paragraph into the message area. Around 400 change events would have been generated, results in over 400 separate assignments, when, as previously stated, only one is required. Not just assignments, either. Don't forget about the overhead behind the scenes in dispatching and handling those change events. This wasteful aspect of data binding can be a major thorn in the side, especially when performance is on the line on slower client machines.

In reality, the wastefulness may not have a profound impact on your application. You may find that the benefits of binding outweigh the cost of using it. However, for those that want a more streamlined approach (sans data binding and web service connector "magic"), one final example needs to be gone through.

Example 3 - Not Using the Web Service Connector or Data Binding
In this last example we'll explore building the same application but without any relying on the web service connector and without the aid of data binding. This is somewhat of a code purist approach and is for those who like being closer to the underlying code.

For this example, start with a blank document and construct the interface almost exactly the same as before. Create two text areas and two buttons on an "interface" layer, give the components the appropriate instance names (msg_ta, morse_ta, msg2morse_btn, morse2msg_btn), and position and size them on the stage accordingly. Leave out the web service connectors since this example doesn't use them.

This next step is very important. By default, the classes needed to access web services are not included in all .swf files. In order to include these classes and leverage their functionality, we need to add them in the following manner.

From the menu bar, select Window -> Other Panels -> Common Libraries -> Classes. Drag the "WebServiceClasses" from the library that opens up onto the stage of your Flash document. Finally, delete the instance on the Stage that you just created. What just happened was that we added the web service classes to the library of our new movie, and by default they will now be included in our published .swf. At this point we can leverage the web service functionality through code. All that's left to do is put the following code on the "action" layer:


import mx.services.WebService;
import mx.services.PendingCall;

var morse_ws:WebService = new
   WebService("http://web221.
   area-18.server-home.net/Morse.
   asmx?WSDL");

msg2morse_btn.onRelease = function() {
	// calling a web service method
   returns a PendingCall
var pc:PendingCall = morse_
   ws.MsgtoMorse(msg_ta.text);
	pc.onResult = function(result) {
		morse_ta.text = result;
	}
	pc.onFault = function(fault) {
		trace("msg2morse fault: " + fault.
   faultString);
	}
}

morse2msg_btn.onRelease = function() {
	var pc:PendingCall = morse_
   ws.MorsetoMsg(morse_ta.text);
	pc.onResult = function(result) {
		msg_ta.text = result;
	}
	pc.onFault = function(fault) {
		trace("morse2msg fault: " + fault.
   faultString);
	}
}

In the code above we create a reference to the web service by passing in the WSDL location to the WebService constructor. Then, whenever a button is pressed we call the appropriate method and save a reference to the PendingCall instance it creates. Finally, the "pc" has two special methods that get invoked when either the results come back successfully (calling "onResult") or when an error occurs (calling "onFault").

Summary and Final Thoughts
As you can see, there is more than one way to accomplish a task. We've built the same application in three distinct ways, each with their pros and cons.

The first example requires the least amount of code. Data binding is leveraged through the Bindings tab in the Component Inspector panel, and two WebServiceConnectors control access to the remote web service.

The second example improves upon the first by placing all of the Data Binding code in a single location, but has the negative side effect of requiring more "hand coding."

The last example is the "down and dirty" approach that uses the WebService and PendingCall classes directly without the need for the WebServiceConnector, and rids us of the wasteful aspect of using data binding.

Each example can be the "right" way to code something, depending on the application requirements and the person doing the coding. I'm a fan of the last example, but you're welcome to disagree.

From here, I encourage you to explore the further reading links. Find a web service that interests you and try building a small application that uses it. Who knows, you might even surprise yourself!

Further Reading

  • XMethods - Web Service Directory: www.xmethods.com/
  • RemoteMethods - Web Service Directory: www.remotemethods.com
  • Flash Developer Center: Web Services Articles: www.macromedia.com/devnet/mx/flash/webservices.html
  • Using the Flash MX 2004 web service classes: www.flash-db.com/services/tutorials/mxclasses/mxwebservices.php
  • Consuming Web services in Flash MX: http://uk.builder.com/architecture/web/0,39026570,20282917,00.htm
  • Understanding Web Services: A List Apart: www.alistapart.com/articles/webservices/
  • Web Services - An Executive Summary: http://webservices.xml.com/pub/a/ws/2002/04/12/execreport.html
  • Flash TechNote - External data not accessible outside a Macromedia Flash movie's domain: www.macromedia.com/cfusion/knowledgebase/index.cfn?id=tn_14213
  • More Stories By Darron J. Schall

    Darron J. Schall, an Editorial Board member of Web Developer's & Designer's Journal, has been programming long before he could drive. In school he studied programming languages, ranging from Basic to Pascal to C++ and eventually moving into Java and C# throughout college. Somewhere in the middle he got hooked on Flash 5 and it's been a crazy love affair ever since. Darron is an independent consultant specializing in RIA development. He maintains a Flash Platform related weblog (www.darronschall.com) and is an active voice in the Flash and Flex communities.

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.