i have in my flex application various mxml components that all need to show stats based on the same data. how can i go about doing that? do i need some mvc framework like cairngrom or puremvc for that or can i do it without them?
any design ideas?
You don't need any framework for that. Do you know about data-binding?
http://www.flexafterdark.com/docs/Flex-Binding
This way you can set your data as dataprovider for many components. For example to show your data in dataGrid you set in mxml it's attribute
dataProvider="{yourDataArrayCollectionIdentifier}"
and in your arrayCollection declaration you need to set metatag [Bindable]
[Bindable] var yourDataArrayCollectionIdentifier : ArrayCollection;
there are other datatypes you can use as dataprovider, just arrayCollection is the most common
There are a handful of approaches to this. For encapsulation purposes, you should isolate your shared data out into a separate class; possibly a Value Object although it does't have to be.
Then create a public variable property in each MXML Component of this classes type. When you create the instance of each mxml component, pass in your 'global' instance of the data class.
You don't need to use an MVC framework, but the Cairngorm Model Locator could also be used to address this problem. The Model Locator is a singleton. You'd store your data inside the singleton instance; and then each MXML Component would reference the singleton for the data. Creating an external dependency like this breaks encapsulation, though. I much prefer the parameter passing route for non-application specific components.
package
{
public class ApplicationViewModel
{
[Bindable] public var message:String = "";
}
}
You can now use this message across your MXML where you make the instance of that.
A singleton class is used in different scenarios where you want to hold some information of all states. A better example would be Chess Board, where your board is Singleton class and its state should never change as you have to keep track of all coins moved across the board and its position.
You are injecting this message variable in the views where you want to show the data.
Related
How can i access a variable or change the state of an element(like tabNavigator) from one mxml to another mxml in FLEX 4.6??
Each separate MXML file should be viewed as a class, since that is what they are.
In the theory of encapsulation; two classes should not directly access / change each others variables or state. They should use an API provided by the developer of the MXML Class.
If MXML 1 is the parent of MXML 2; then MXML1 can pass data to MXML2 by setting public properties or calling public methods.
MXML2 can pass data to MXML1 by dispatching events.
If MXML1 and MXML2 are not in a parent child relationship; (AKA Both children of the same component as one example) they they should not communicate with each other directly. They should dispatch events which the mutual parent should handle and use to set values or execute methods on it's own children.
From an encapsulation standpoint, that is how it should be done using the built in facilities of ActionScript / Flex.
What a lot of people do as part of building applications is to make use of dependency injection. That was values that are "global to the application" can be shared among multiple components. Another approach to doing this is to use a Singleton. A third approach might be to make use of static values on a class; which can be accessed without accessing an instance of a class.
Im using Oracle, BlazeDS, Java & Flex. I have an ArrayCollection containing data from a small database table. This table won't be the subject of much change. I want to use this ArrayCollection accross different mxml files to fill e.g. ComboBoxes etc.
The reason for asking, is that doing a database call for each time a fill a ComboBox etc is slow and seems unnecessary. I tried doing this once in the "main" .mxml file, but then the variable wasn't accessible where i needed it.
What is the best approach for accomplishing this task? What is the best way of making a variable accesible across .mxml files? :)
[Bindable] public static var yourArrayCollection:ArrayCollection
That should make it visible anywhere but using static variables is normally not a good idea.
You could also implement a singleton instance to persist a variable if you do not want to make it static and need to reference other functions etc - but I think the static variable should do fine.
If this is a larger application, I'd recommend looking at Parsley: http://www.spicefactory.org/parsley/. With Parsley, you could add the array collection to the context and simply inject it whenever you need to reference it. The array collection should be populated during application startup and can be updated as needed.
There basically are two ways. The singleton way, and the static class way. A singleton is a class that is only instanciated once, through a mechanism described here, for instance. A static class is a bit different from a regular class : you will not instanciate it, first of all.
For more information about how implement a singleton in ActionScript 3 : here.
For more information about static classes and variables : here.
You can just make it public member of some class and import that class in all MXML-based classes:
public class DBWrapper {
[Bindable]
public var ItemList:ArrayCollection;
}
I usually make it a static member of a Globals class
public class Globals {
[Bindable] public var iCollection:ArrayCollection;
}
It can be accessed from anywhere in the program (provided you have assigned a valid ArrayCollection to it first)
combobox.dataProvider=Globals.iCollection;
I made a custom itemRenderer, and need to access a variable in my Project file (mxml). How can I make my public var available in the custom itemRenderer file?
public function lang_f(trans_short:String):String{
if(outerDocument.language == "de"){
...
}
}
Greetings
Markus
You shouldn't access outer document data from within item renderer. It breaks OOP principles such as low coupling etc. You should either set this data to item renderer with data from data provider or extend your list based component to instantiate renderers with that data. And don't think about MXML component as files. The file structure is just a form of representation. You'd better think MXML files as classes or components — in terms of application structure.
To go along with Constantiner's answer, if you still need to access that variable, you can access the variable in your itemRenderer using outerDocument:
outerDocument.myVariableName
Is the variable defined in main file of your application ? If that's the case you can access is using FlexGlobals.topLevelApplication.YOUR_VARIABLE_NAME
However I agree with Constantiner. This violation of OOP principles. But I hope that solves your problem.
I'm re-writing an MXML item renderer in pure AS. A problem I can't seem to get past is how to have each item renderer react to a change on a static property on the item renderer class. In the MXML version, I have the following binding set up on the item renderer:
instanceProperty={callInstanceFunction(ItemRenderer.staticProperty)}
What would be the equivalent way of setting this up in AS (using BindingUtils, I assume)?
UPDATE:
So I thought the following wasn't working, but it appears as if Flex is suppressing errors thrown in the instanceFunction, making it appear as if the binding itself is bad.
BindingUtils.bindSetter(instanceFunction, ItemRenderer, "staticProperty");
However, when instanceFunction is called, already initialized variables on the given instance are all null, which was the cause of the errors referenced above. Any ideas why this is?
You have 2 options that I am aware of:
Option 1
You can dig into the code that the flex compiler builds based on your MXML to see how it handles binding to static properties. There is a compiler directive called -keep-generated-actionscript that will cause generated files to stick around. Sleuthing through these can give you an idea what happens. This option will involve instantiating Binding objects and StaticPropertyWatcher objects.
Option 2
There is staticEventDispatcher object that gets added at build time to classes containing static variables see this post http://thecomcor.blogspot.com/2008/07/adobe-flex-undocumented-buildin.html. According to the post, this object only gets added based on the presence of static variables and not getter functions.
Example of Option 2
Say we have a class named MyClassContainingStaticVariable with a static variable named MyStaticVariable and another variable someobject.somearrayproperty that we want to get updated whenever MyStaticVariable changes.
Class(MyClassContainingStaticVariable).staticEventDispatcher.addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
function(event:PropertyChangeEvent):void
{
if(event.property == "MyStaticVariable")
{
someobject.somearrayproperty = event.newValue as Array;
}
});
I think you need to respond to the "PropertyChanged" event.
If you're going to do that, use a singleton instead of static. I don't think it will work on a static. (If you have to do it that way at all, there are probably a couple ways you could reapproach this that would be better).
var instance:ItemRenderer = ItemRenderer.getInstance();
BindingUtils.bindProperty(this, "myProperty", instance, "theirProperty");
After fiddling with this for a while, I have concluded that this currently isn't possible in ActionScript, not even with bindSetter. It seems there are some MXML-only features of data bindings judging by the following excerpt from the Adobe docs (though isn't it all compiled to AS code anyways)?
You cannot include functions or array
elements in property chains in a data
binding expression defined by the
bindProperty() or bindSetter() method.
For more information on property
chains, see Working with bindable
property chains.
Source: http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_7.html
You can create a HostProxy class to stand in for the funciton call. Sort of like a HostFunctionProxy class which extends from proxy, and has a getProperty("functionInvokeStringWithParameters") which will invoke the function remotely from the host, and dispatch a "change" event to trigger the binding in typical [Bindable("change")] Proxy class.
You than let the HostProxy class act as the host, and use the property to remotely trigger the function call. Of course, it'd be cooler to have some TypeHelperUtil to allow converting raw string values to serialized type values at runtime for method parameters (splitted by commas usually).
Example:
eg.
var standInHost:Object = new HostFunctionProxy(someModelClassWithMethod, "theMethodToCall(20,11)");
// With BindingUtils.....
// bind host: standInHost
// bind property: "theMethodToCall(20,11)"
Of course, you nee to create such a utlity to help support such functionality beyond the basic Flex prescription. It seems many of such (more advanced) Flex bindings are usually done at compile time, but now you have to create code to do this at runtime in a completely cross-platform Actionscript manner without relying on the Flex framework.
I've only done a bit of Flex development thus far, but I've preferred the approach of creating controls programmatically over mxml files, because (and please, correct me if I'm wrong!) I've gathered that you can't have it both ways -- that is to say, have the class functionality in a separate ActionScript class file but have the contained elements declared in mxml.
There doesn't seem to be much of a difference productivity-wise, but doing data binding programmatically seems somewhat less than trivial. I took a look at how the mxml compiler transforms the data binding expressions. The result is a bunch of generated callbacks and a lot more lines than in the mxml representation. So here's the question: is there a way to do data binding programmatically that doesn't involve a world of hurt?
Don't be afraid of MXML. It's great for laying out views. If you write your own reusable components then writing them in ActionScript may sometimes give you a little more control, but for non-reusable views MXML is much better. It's more terse, bindings are extemely easy to set up, etc.
However, bindings in pure ActionScript need not be that much of a pain. It will never be as simple as in MXML where a lot of things are done for you, but it can be done with not too much effort.
What you have is BindingUtils and it's methods bindSetter and bindProperty. I almost always use the former, since I usually want to do some work, or call invalidateProperties when values change, I almost never just want to set a property.
What you need to know is that these two return an object of the type ChangeWatcher, if you want to remove the binding for some reason, you have to hold on to this object. This is what makes manual bindings in ActionScript a little less convenient than those in MXML.
Let's start with a simple example:
BindingUtils.bindSetter(nameChanged, selectedEmployee, "name");
This sets up a binding that will call the method nameChanged when the name property on the object in the variable selectedEmployee changes. The nameChanged method will recieve the new value of the name property as an argument, so it should look like this:
private function nameChanged( newName : String ) : void
The problem with this simple example is that once you have set up this binding it will fire each time the property of the specified object changes. The value of the variable selectedEmployee may change, but the binding is still set up for the object that the variable pointed to before.
There are two ways to solve this: either to keep the ChangeWatcher returned by BindingUtils.bindSetter around and call unwatch on it when you want to remove the binding (and then setting up a new binding instead), or bind to yourself. I'll show you the first option first, and then explain what I mean by binding to yourself.
The currentEmployee could be made into a getter/setter pair and implemented like this (only showing the setter):
public function set currentEmployee( employee : Employee ) : void {
if ( _currentEmployee != employee ) {
if ( _currentEmployee != null ) {
currentEmployeeNameCW.unwatch();
}
_currentEmployee = employee;
if ( _currentEmployee != null ) {
currentEmployeeNameCW = BindingUtils.bindSetter(currentEmployeeNameChanged, _currentEmployee, "name");
}
}
}
What happens is that when the currentEmployee property is set it looks to see if there was a previous value, and if so removes the binding for that object (currentEmployeeNameCW.unwatch()), then it sets the private variable, and unless the new value was null sets up a new binding for the name property. Most importantly it saves the ChangeWatcher returned by the binding call.
This is a basic binding pattern and I think it works fine. There is, however, a trick that can be used to make it a bit simpler. You can bind to yourself instead. Instead of setting up and removing bindings each time the currentEmployee property changes you can have the binding system do it for you. In your creationComplete handler (or constructor or at least some time early) you can set up a binding like so:
BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]);
This sets up a binding not only to the currentEmployee property on this, but also to the name property on this object. So anytime either changes the method currentEmployeeNameChanged will be called. There's no need to save the ChangeWatcher because the binding will never have to be removed.
The second solution works in many cases, but I've found that the first one is sometimes necessary, especially when working with bindings in non-view classes (since this has to be an event dispatcher and the currentEmployee has to be bindable for it to work).
It exists as of today. :)
I just released my ActionScript data binding project as open source: http://code.google.com/p/bindage-tools
BindageTools is an alternative to BindingUtils (see the play on words there?) that uses a fluent API where you declare your data bindings in a pipeline style:
Bind.fromProperty(person, "firstName")
.toProperty(firstNameInput, "text");
Two-way bindings:
Bind.twoWay(
Bind.fromProperty(person, "firstName"),
Bind.fromProperty(firstNameInput, "text"));
Explicit data conversion and validation:
Bind.twoWay(
Bind.fromProperty(person, "age")
.convert(valueToString()),
Bind.fromProperty(ageInput, "text")
.validate(isNumeric()) // (Hamcrest-as3 matcher)
.convert(toNumber()));
Etc. There are lots more examples on the site. There's lots of other features too-come have a look. --Matthew
Edit: updated APIs
One way to separate the MXML and ActionScript for a component into separate files is by doing something similar to the ASP.Net 1.x code behind model. In this model the declarative part (the MXML in this case) is a subclass of the imperative part (the ActionScript). So I might declare the code behind for a class like this:
package CustomComponents
{
import mx.containers.*;
import mx.controls.*;
import flash.events.Event;
public class MyCanvasCode extends Canvas
{
public var myLabel : Label;
protected function onInitialize(event : Event):void
{
MyLabel.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
}
}
}
...and the markup like this:
<?xml version="1.0" encoding="utf-8"?>
<MyCanvasCode xmlns="CustomComponents.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="onInitialize(event)">
<mx:Label id="myLabel"/>
</MyCanvasCode>
As you can see from this example, a disadvatage of this approach is that you have to declare controls like myLabel in both files.
there is a way that I usually use to use mxml and action script together: All my mxml components inherit from a action script class where I add the more complex code. Then you can refer to event listeners implemented in this class in the mxml file.
Regards,
Ruth