So in general - Flex has been easy for me to grok since it first came out. It's gotten easier over time, but normally, the most trouble I have is having to look up an argument to see how to do something in particular.
However - one thing has troubled me for a while now. I've solved this problem before in earlier Flex apps, but I really don't think I have my head around it yet. I'm asking for help and some opinions here on what would be the 'best practice', and I more than understand that there will be more than one answer.
So first off - let me describe the problem. I know that a Flex application can be entirely contained within one MXML file. That can get a bit messy though. So what I like to do is create a views folder. In my main MXML file I add xmlns:views="views.*" to my core Application tag. This then lets me do:
<views:login />
<views:foo />
<views:nuclearbomb />
I get that and I like it as it really makes things more manageable. However - I run into a problem as soon as I want to start sharing data between multiple views. For example, my main MXML has code like so (slightly cropped/edited):
<mx:RemoteObject id="dataService" destination="ColdFusion" source="remote.user" showBusyCursor="true">
<mx:method name="login" result="loginResult(event)" />
</mx:RemoteObject>
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
//ToDo: Abstract this into an XML file
private var endpoint:String = "http://dev.nextbigthing.com/flex2gateway/";
private function init():void {
dataService.endpoint = endpoint;
}
]]>
</mx:Script>
Notice the init function, which is run on creationComplete. My thinking was that this config info would actually be moved into an XML file and loaded in via a file read (this is an AIR app by the way). I figured this would be a nice way to configure the application. Anyway - this works like a charm. I can run a method on my dataService.
But what happens when I want to use this same (configured) service in a lower view? Well I figured out that I could pass in the RemoteObject easily enough:
<views:someview dsConn="{dataService}" />
And inside someview.mxml:
[Bindable] public var dsConn:RemoteObject;
But now I want to run a method on my remote service. I want method X to use call back handler Y. Normally I'd define that with a mx:method tag. However, if I do that in the top level component I should get an error if my call back handler is in someview.mxml. It seems to me that it would be best to keep all the code someview needs within someview itself - but I'm at a loss as to how to properly handle that.
As a practical example, someview.mxml has a grid and it needs data. The data comes from the RemoteObject. Am I expected to create the data in the top level MXML and pass it to someview? That seems... ok... but it feels like I'm cluttering up someview. And to make matters worse, what I really have is main.mxml, which is under my root level MXML, and someview is under main.mxml. So I'd need to pass things along two 'levels'. Ugh.
I really think I'm over complicating this. So Flex-lovers - tell me there is a simpler way to handle all of this. ;)
Archived Comments
To be honest Ray, you really need to take an MVC approach to this - by putting remote objects into your view you're breaking all sorts of best-practise. Therefore, I would strongly advise that you do one of two things:
a) Create a services component that contains your service declaration and configuration, plus any required helper functions - you would then include this where needed (or make a global singleton object for it somehow)....or
b) don't reinvent the wheel and use a framework like Cairngorm. Knowing that you know model-glue well, this should be easy to pick up assuming you've nailed the conceptual model in your head. For this, I find that the Cairngorm diagram* invaluable. Cairngorm isn't scary, and solves so many problems like this in a really simple best-practise way.
* http://www.cairngormdocs.or...
Check out puremvc. I've used cairngorm and puremvc and i like puremvc a lot more.
Neil is right. You probably need a bit of architecture, MVC style. Once an app moves beyond trivial, the appo will eventually trip over itself and need a refactor.
Cairngorm is one way to do it, PureMVC (puremvc.org) would be another.
The dead simplest way would be EasyMVC by Simeon Bateman. This will get you up to speed the quickest.
http://projects.simb.net/ea...
The screencast (http://blog.simb.net/2007/1... will get you started in 7 minutes flat.
Regardless of what you choose, the answer to your problems is a proper architecture.
DW
I completely agree with all of these remarks. It may cost you a little time now, but compartmentalizing everything now will definitely save you time (and frustration) later.
Agree with previous remarks. Take a look at Easy MVC (Tom Bray as opposed to Simeon's EasyMVC) - it is a lot quicker to get your head around than Cairngorm and PureMVC. What I like about Tom Bray version is that you do not have to download/include anything - it is an easy to implement approach. Nicely explained/documented by Jon Baker here:
http://www.clockobj.co.uk/2...
You don't need to change your entire approach/setup for this. Just create a Singleton which handles all of your data transfer then hit it like this:
DataLocator.instance.dataService.someMethod();
(or DataLocator.getInstance(); your preference)
You can then use the same dataService throughout the site without passing it all over the place.
Now, for keeping it on the top level and passing events down...no sweat there either. You shouldn't get any errors if you set
mx:method ... result="something.someFunction(event)"
Just make sure your someFunction expects the proper result type.
Hit me up on IM and we can hash out some more ideas/updates. MVC is great but for this you don't need to rewrite your app to use MVC.
@All: I've avoided considering Flex frameworks as I feel like I'm not ready for them yet. I feel like I need to learn more. But at the same time - I avoided learning CF frameworks for a while too, so maybe I just need to get the heck over it.
@JBII: I had assumed it would throw an error. I'll give that a try as well.
@Ray I found learning Cairngorm taught me helluva lot about Flex - and that helps you write better code elsewhere..
"Am I expected to create the data in the top level MXML and pass it to someview? "
You'll want to create a model or value object to hold your data. At the top of either Model or Value Object class, add a [Bindable] Tag.
When your data is received from CF, you can now populate the Value Object or Model 's properties (for example, usersFirstName) with the data response and reference this data from any of your views of their subclasses.
In your views, you can use the mx:Binding tags to bind a control to and from your control (ex: textInput.text) to your data in the model or Value Object. Now, the views will update automatically when the data changes, and you have one reference point for the current data (Model or VO).
When you save the data, you can just pass the Value Object to CF and work with it as a Struct, or you can cast your model's properties to an object's properties.
To see errors, make sure you're working with the Flash Debug Player, and not just the Flash Player. For your fault handler in a Remote Object, make sure you add a throw statement in actionscript. If I remember right, throw(faultevent.error.toString()) will give you added detail, but I'd doublecheck this.
//ToDo: Abstract this into an XML file
private var endpoint:String = "http://dev.nextbigthing.com...";
Coldfusion already has this set-up. You can use the Flex Config.xml and Services-Config.xml files located in the web-inf/flex files, to create named service definitions.
If these files aren't viewable, you'll need to generate them using the compiler args in Flex.
David - I'm building an AIR app so I think this is necessary, is it not?
Because the Air App still makes named Service calls to a remote object on the server, the deployment type (Flex or Air) should not matter. The Flex-Config file should be compiled into the SWF.
I'm confused though - so your saying I should use the flex-config file to name my service and configure the URL there? I inteded to have other settings as well, not just the end point, and I'd like to keep it all together if possible, so is there anything wrong with my approach?
There are some different configuration files for AIR:
"The only difference between the Flex and the AIR versions of the utilities is that the AIR versions load the configuration options from the air-config.xml file instead of the flex-config.xml file."
http://livedocs.adobe.com/f...
"I'm confused though - so your saying I should use the flex-config file to name my service and configure the URL there?"
Yes, but according to the docs, you'll need to use the Air-Config file. I just googled it around, so it's new t me.
You can dump all of the Config files using...:
http://livedocs.adobe.com/f...
I would assume the AIR-config file has similar destination path configurations as the flex-config.xml file.
If you use Cairngorm, you'll have a Services.mxml file. This is the Cairngorm way of referencing the External named services (also known as CFCs). You can also reference the Named Services without the Services.MXML file that Cairngorm provides, by removing your endpoint and adding the Name of your service (as configured outside of Flex) in the Remote Destination.
The question one might ask if this: Why should I use the Flex-Config and Services-Config.xml files, when I can do the exact same thing inside my view with Actionscript/mxml?
The answer is: Now, you can change the destination paths and roles without having to recompile your SWF. Also, you'll probably find your paths to your CFCs is different from Dev as it is in Production, and being able to compile against two different XML files (production vs dev) makes life easier.
"I inteded to have other settings as well, not just the end point, and I'd like to keep it all together if possible, so is there anything wrong with my approach?"
While I'm not sure what the other settings are, you can add additional settings in the external configuration file.
As Todd Rafferty pointed out to me last week, the Coldfusion 8.0.1 update affects the security loophole with Flex being able to access Public vs Remote CF functions.
These are the types of settings you could change in the Config files (what kind (access) and which (function names) can flex call). Roles can also be configured in the Config files, as well as a whitelist of acceptable services.
This may be of help, too:
http://www.firemoss.com/blo...
The greatest difficulty in understanding the process is a lack of good, working Flex/Air examples for CF (and some of the other languages). Understanding the relationship between Flex, Cairngorm, and CF through Standard Remoting is complex without being able to visualize it, because some of the files you should use or need, don't exist out-of-the-box...instead...they have to be "Generated."
IMO, the Cairngorm Store example is just too complex, and the Login example is just too basic.
If you have any issues, I can whip up a working Flex/Cairngorm app for you, given the basics of your current structure, that include the Remoting-Config and Services-Config files. Cairngorm, like Fusebox, gives you the ability to copy and paste existing files, to create new functionality:
Copy View
Cut out old code in view down to the Cairngorm Event call and Cairngorm imports.
Create Event. Add variable you'll be passing.
Add Event Name & Command Name to controller.
Create Command.
Create Delegate.
Create Service in Delegate (and Services Config file if it doesn't exist).
Rinse. Wash. Repeat.
You'd have to change the flex-config to the air-config, though. :)
I usually skip the Cairngorm framework when I'm building something as simple as a Contact Form in Flex. If I'm going to make more than 3 different method calls, that's when I'll start to separate out the logic into an MVC structure.
Note: according to the doc linked below, the Flash Debugger does not apply to AIR, but adding try/catch/throw (or trace) statements is strongly encouraged. Unlike CF and Java, Flex doesn't have a global way (like application.cfc's onError) to trap and log errors. It's granular...method by method.
http://livedocs.adobe.com/f...
Actually, I take this back:
"The answer is: Now, you can change the destination paths and roles without having to recompile your SWF. Also, you'll probably find your paths to your CFCs is different from Dev as it is in Production, and being able to compile against two different XML files (production vs dev) makes life easier."
It still gets compiled into the application and the SWF has to be recompiled for changes. What I meant to say was:
"Now, you can change the destination paths and roles without having to edit paths in your XML file since your paths to your CFCs are different from Dev to Production, and being able to compile against two different XML files (production vs dev) makes life easier."
Need 3 Flex engineers in New York City:
1. You have to be living in New York City for now.
2. Work part time or full time with us.
3. Good at Adobe Flex technology.
4. Please contact us for other requirement and details.
Busycode Inc. is a top Adobe Flex shop who develops Flex/AIR applications for clients.
For more info, please visit http://www.busycode.com
Andy - I will leave your comment up for now, but in the future, do NOT post job postings like this. My blog is not your job board and while my post concerns Flex, your job posting is NOT on topic.
Ray, I've also avoided Flex frameworks so far for the same reason as you. I solve the RemteObject problem by taking an event-driven approach. My apps aren't overly complex, so I'll often keep my RO in the root MXML, and have my views broadcast custom (often bubbling) events which are caught and handled in the root. I also employ a ModelLocator and often use the ChangeWatcher to trigger those events. It's not a pure MVC approach, but it's been a good learning experience, and I can understand exactly what's going on. If you're looking to get your hands dirty before diving into a framework, custom events are a great way to do it! :)
I'm with you Ken. I have my own approach to Flex apps which include design patterns but I don't use frameworks. Think about it...Flex is a FRAMEWORK. You're putting a framework on top of a framework. That's fine, honestly it is, but unnecessary. Yes, maybe it will speed you up a bit but spend 6 months writing apps and you'll have your own basic code-base ("framework").
That's just the way I choose to do it. PureMVC, etc is not a end-all-be-all for every RIA you touch. It is a peg which you have to find the right hole to fit into.
I see the general consensus is do what feel best for you and personally I have found frameworks invaluable to by Flex application development in particular PureMVC. Each to their own :) Regarding you query Ray I have developed a demo Flex CF application doing a simple CFC query and have taken a slightly different approach creating a remote delegate class thats reutilised for soooo easily for all method calls on my cfc. This, as I have read above, may seem all a little ott but to be honest this was easy to do and I also wrote a tutorial on it for a publication over here in the UK and a ot of developers with different ranges of capabilities picked it up np. Anyway, the full source can be checked out here on my project owner page, see what you think :)
http://trac.puremvc.org/Dem...
Cheers
Simon
Ray, you can use bits and pieces of the cairngorm framework until you fully understand all parts of it. Take a look at this article by Tom Ortega when you are ready. I found it invaluable. http://www.adobe.com/devnet...
I have found all of these comments invaluable.