Sharing variables between mxml components - apache-flex

I have several mxml components in an app, all of which need the same variable called genericX. I've included that variable in the main mxml and made it public
[Bindable] public var genericX:Number = 102;
but I still can't access it from other mxml components. If I try to do this for example, it doesn't recognize the variable.
<s:Button x="{genericX}" label="Click" />

There's also a filthy solution that works but isn't nice. You can create a static variable against the application class. For example:
[Bindable] public static var genericX : Object
You can access that from anywhere like this:
MyApplicationName.genericX
It ain't pretty, but it does work :)
simon

You cannot access in this way. There is something called Events in Flex and you need to pass this variable in a MXML file to another using eventDispatcher.
For example
[Bindable] public var genericX:Number = 102;
private function init():void {
var evt:NewCustomEvent = new CustomEvent(CustomEvent.SENDDATA);
evt.genericaValue = genericX
dispatchEvent(evt);
}
Now you need to get into the MXML component where you want to recieve this Event and using addEventListner() to recieve this event and the corresponding variable.
Then finally Inject it into your button.

You should be able to access any global variables with:
Flex 3:
var app:Application = mx.core.Application.application as Application;
Flex 4(looks like what you're using):
var app:Object = FlexGlobals.topLevelApplication;
And then:
<s:Button x="{app.genericX}" label="Click" />

x="{parentApplication.genericX}"

Here is an example for sharing variables between MXML components by declaring them public in the main application.

Related

Adding <fx:Declarations> via action script

I have an mxml page that has this tag:
<fx:Declarations>
<mx:StringValidator id = "validator"
source = "{myTextInput}"
property = "text"
required = "true"
maxLength = "128"/>
<fx:Declarations>
I want to do the same in another page but build and add the validator dynamically using action script. I have this code for building the validator:
var lengthTextValidator:StringValidator = new StringValidator();
lengthTextValidator.source = fieldTextInput;
lengthTextValidator.property = "text";
lengthTextValidator.required = true;
How can I finish the work and add the validator to the page? Thanks!
To add a UIComponent as a child of another UIComponent you can use addChild():
myComponent.addChild(myOtherUIComponent);
However, a validator is not a UIComponent or DisplayObject. It does not get added as a child to a page. If you are just replacing the fx:Declaration piece of an MXML file with an ActionScript piece that does the same thing, then you don't have to do much more.
I would make the lengthTextValidator a public, or protected, instance variable on the component:
public var lengthTextValidator:StringValidator = new StringValidator();
That means the variable will be use within the component. Your original syntax without the public or private will either make a method specific variable that won't be accessible when the method is done executing or will put the variable in the internal namespace.
The rest of your code must go in a method. For an ActionScript class; you can put it in a constructor. Or for a MXML class you can put it in an initialize or creationComplete event handler.
lengthTextValidator.source = fieldTextInput;
lengthTextValidator.property = "text";
lengthTextValidator.required = true;
If you are putting the validator code in a separate class; then you'll have to import the class and create an instance of it:
import myPackage.MyClass;
public var myClass :MyClass = new MyClass();
Then you can access the validator by accessing the public variable on the component:
myClass.lengthTextValidator;
Finally; if you just want to move that snippet into an ActionScript file that is not a class; you can use the include directoive inside a fx:Script block:
<fx:Script><[[
include "myASFile.as"
]]></fx:Script>
The last approach is unorthodox and not usually recommended.

Flex - Passing data into tabbed views

I have a project that has 4 views where I'm using the tabBar with viewStack/NavigatorContent. I have a couple HTTPServices set up gathering the XML and converting to Bindable ArrayCollections. When and how is the best way to pass that data to such things as charts, dataGrids, etc. that aren't seen until the user clicks a tab? Right now I have each item set up with creationComplete functions that pass it then. Although it seems to be working, is this the best way, or is there something better and maybe even quicker?
Thanks,
Mark
The best way is using data binding. Say you have a main container which contains ViewStack witch components representing tabs content. So you should have [Bindable] properties for data in a main container like the following:
[Bindable]
private var chartData:ArrayCollection;
[Bindable]
private var dataGridData:ArrayCollection;
etc.
So for the component, containing chart, you should populate chart data with data binding:
<ViewStack>
…
<MyChartsTab chartData="{chartData}" />
…
</ViewStack>
And of course you should introduce the same chartData field (make sure it is public) in your MyChartsTab component. Your charts there can be populated with data binding too.
So after getting data you just fill your fields in main component and data binding performs the rest job without any care of initialization from your side.
When creating your views, make sure you allow a public variable (like 'dataProvider') where you can bind the data it needs. Like this:
<mx:ViewStack>
<s:NavigatorContent>
<SomeComponent dataProvider="{someData}" />
</s:NavigatorContent>
<s:NavigatorContent>
<SomeComponent2 dataProvider="{someData}" />
</s:NavigatorContent>
</mx:ViewStack>
And within the custom component you'd have:
private var _data:Object; // use strong typing if possible
public function set dataProvider(value:Object):void // use strong typing if possible
{
this._data = value;
}
public function get dataProvider():Object
{
return this._data;
}

Watching a bindable property

In my flex app I have a public bindable property.
I want it so that every time the value of that property changes, a function gets triggered.
I tried using ChangeWatchers, but it seems those only apply to built-in components like a text box change.
I would like to do that same behavior with a property that changes at runtime.
One option is to use BindingUtils.bindSetter (which incidentally returns a ChangeWatcher):
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="this_creationComplete()">
<mx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import mx.binding.utils.ChangeWatcher;
[Bindable]
public var myValue:int = 0;
private function this_creationComplete():void
{
var cw:ChangeWatcher = BindingUtils.bindSetter(myValueChanged, this, "myValue");
}
private function setValue():void
{
myValue = getTimer();
}
private function myValueChanged(o:Object):void
{
trace("myValue: " + myValue.toString());
// You can also use o.toString() -- the new value will be passed into the function
}
]]>
</mx:Script>
<mx:Button label="Click Me" click="setValue()" />
</mx:Application>
Here, myValueChanged gets called whenever the myValue property changes. There are other ways, of course, but I often use this approach with good results. Hope it helps! Post back with questions and I'll keep an eye out.
Look into BindUtils class as back2dos suggests.
And, also, you can set the name of the event that will be triggered when a change is done to a property (default is propertyChange) like this:
[Bindable("change")]
var myProperty : SomeClass;
That is if ChangeWatchers adds listeners for the change event instead of propertyChange event. Which would be kind of weird, but not impossible with all the mishaps of the flex SDKs.
But again, I think BindUtils class should do the trick for you.
Use the class ObjectProxy or its subclass and wrap up the class that has a property you need to watch. In my example, I'm calling a func if someone is changing the property salary giving it a value of more than 55000 in an object Person:
package com.farata
{
import mx.utils.ObjectProxy;
import flash.utils.*;
use namespace flash_proxy;
public dynamic class MyPersonProxy extends ObjectProxy
{
// The object to wrap up
private var person:Person;
public function MyPersonProxy(item:Person){
super(item);
person=item;
}
flash_proxy override function setProperty(name:*, value:*):void {
if ( name == 'salary'&& value>55000) {
// add a new property to this instance of the
// class Person, which can be used in the calculations
// of the total compensation
setProperty("pension", 0.02);
}
super.setProperty(name, value);
}
}
}
well, the easiest way is to listen to PropertyChangeEvent.PROPERTY_CHANGE ... if you declare a property bindable, then mxmlc generates the code to dispatch this event ... if you let the compiler keep the generated ActionScript, then you'll see it ...
other than that, you might want to have a look at BindingUtils ...

Access to elements defined in MXML from External AS

I have a MXML with a form, and inside it, two TextInputs. I hate having any piece of code inside the MXML file (I come from a JavaScript formation) so I use a
mx:Script source="external.as"
tag to include any code used in any MXML file. The problem is that if I have this code on the external.as file:
private function populateFromForm():void{
var vo:ValidObject= new ValidObject();
vo.market = marketInput.text;
vo.segment = segmentInput.text;
vo.priceLow = priceLowInput.text;
vo.priceHigh = priceHighInput.text;
}
Where marketInput, segmentInput, priceLowInput and priceHighInput are TextInputs defined in the MXML file. When I try to complile I get a 1120: Access to undefined property XXXXX
I have tried adding this lines prior to the function:
public var marketInput:TextInput;
public var segmentInput:TextInput;
public var priceLowInput:TextInput;
public var priceHighInput:TextInput;
But instead I get a 1151:A conflict exists with definition XXXX in namespace internal which makes perfect sense.
Is there a way to do this without having to pass all the input references to the function as parameters of it?
You need to create a reference to an instance of the TextInputs' parent container, and then use that reference to accsess the TextInputs and their properties. I think we need some clarification on your file structure. How are you creating the instance of the parent container? I'm thinking this is what you need to do:
MyForm.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:TextInput id="marketInput" />
<mx:TextInput id="segmentInput" />
<mx:TextInput id="priceLowInput" />
<mx:TextInput id="priceHighInput" />
</mx:VBox>
SaveVOContainer.as:
package
{
public class SaveVoContainer extends Container
{
private var myForm:MyForm = new MyForm();
public function SaveVOContainer
{
this.addChild(myForm);
}
private function populateFromForm():void{
var vo:ValidObject= new ValidObject();
vo.market = myForm.marketInput.text;
vo.segment = myForm.segmentInput.text;
vo.priceLow = myForm.priceLowInput.text;
vo.priceHigh = myForm.priceHighInput.text;
}
}
}
Doing a "code-behind" is painful in Flex. There is no concept of partial classes or the flexibility of prototypal inheritance as in Javascript. Google for "code-behind in flex" for many resources.
I think it's better you get used to the idea of embedding code in mxml. Use script tags avoiding inline code as much as possible. If you have to write a lot of code within MXML, perhaps you may want to re-factor the code into multiple custom components. Bonus points if they are reusable.
The canonical way to do code-behind in Flex is via inheritance. Here's a good explanation from the docs: http://learn.adobe.com/wiki/display/Flex/Code+Behind. In a nutshell:
Declare an ActionScript class to use as your base class.
Set the base class as the root container in your MXML file.
For any controls declared in your MXML file, you have to redeclare them as public members of the base class using the exact same name (exactly as you are doing above for your script block with source tag, only it works :-)
So, your ActionScript file:
package mypackage
{
import mx.controls.TextInput;
public class myClass extends WindowedApplication
{
public var marketInput:TextInput;
private function populateFromForm():void{
/* As above */
}
}
}
And the corresponding MXML file:
<?xml version="1.0" encoding="utf-8"?>
<custom:myClass xmlns:custom="mypackage.*"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<mx:TextInput id="marketInput"/>
</custom:myClass>
Et cetera for your other TextInput controls. And now your populateFromForm function should work.
It is kind of heinous to have to redeclare the same entities twice, but it's not quite the bag of hurt the earlier respondent made it out to be (although it's possible this changed in Flex 4 to make it less painful than it was).
import this in .AS:
import mx.core.Application;
in the .AS use this:
mx.core.Application.application.component.property = value;
mx.core.Application.application.myText.text = 'test';
do you have a script tag in your mxml file that points to your ActionScript file?
<mx:Script source='includes/foo.as' />

What restrictions does public/private have in actionscript functions?

I'm currently maintaining some flex code and noticed very many functions which are declared like:
private function exampleFunc():void {
....
}
These functions are in the global scope, and aren't part of any specific class, so it's a bit unclear to me what effect declaring them as private would have. What restrictions does the "private" qualifier have for functions like this?
The actionscript functions that are included in your mxmlc code will we available as a part of your mxmlc component, which behind the scenes is compiled into a class. Therefore marking them as private makes them inaccessible.
Here is an example to make that clear, say you have the following component, we'll call it FooBox:
<!-- FooBox.mxml -->
<mx:Box xmlns:mx="http://www.macromedia.com/2003/mxml">
<mx:Script><![CDATA[
private function foo():void {
lbl.text = "foo";
}
public function bar():void {
lbl.text = "bar";
}
]]></mx:Sctipt>
<mx:Label id="lbl">
</mx:Box>
I can now add FooBox to my application, and use it's functions:
<mx:Application
xmlns:mx="http://www.macromedia.com/2003/mxml"
xmlns:cc="controls.*"
>
<mx:Script><![CDATA[
private function init():void {
fbox.foo(); // opps, this function is unaccessible.
fbox.bar(); // this is ok...
}
]]></mx:Sctipt>
<cc:FooBox id="fbox" />
</mx:Application>
If the actionscript functions are included in your Main Application file, the I think you can call the functions from an child control through the Application.application object, something like:
Application.application.bar();
if the bar function was placed in the main mxmlc code.
What do you mean by global scope? Are these functions declared in the main MXML file?
In general, private means that functions can only be called from within the class that declares them.
But, when you put it in a actionscript file .as is it still compliled into a class ?
Because asdoc doesn't like it.

Resources