I'm creating a mx:tree with Flex 4 and the tree is being populated with an XMLList file, the file is updated from a database and what I'm trying to accomplish is to refresh the tree so that a different icon is shown depending on the state of completion of a course.
The problem is that the tree is not being updated but the XMLList file is, so I would really appreciate some help.
By the way, I don't know about Flex.
Here's the code:
public static var treeData:XMLList = new XMLList(MyString);
<mx:Tree id="myTree" width="40%" height="100%" labelField="#label" fontSize="14" focusColor="#ff5003"
render="renderTree()"
iconFunction="tree_iconFunc"
showRoot="false"
dataProvider="{treeData}"
change="treeChanged(event)"
depthColors="{myDepthColors}"
color="#006596" borderColor="#03B4EC"
click="SoundExample(String(selectedNode.#lesson)), habilitar()"
alternatingItemColors="{myAlternatingRowColors}"
/>
public function renderTree():void {
trace("EntrĂ³ a renderTree");
initAppB();
if (refreshData){
myTree.invalidateList();
refreshData = false;
myTree.openItems = Globals.treeData;
myTree.validateNow();
}
}
Instead XMLList you could use bindable collection like ArrayCollection.
With it you don't need to invalidate tree manually. It will be updated automatically when ArrayCollection changes.
Also I'm not sure what do you mean by "the tree is being populated with an XMLList file, the file is updated from a database". Could you clarify?
To update a tree you have to update treeData:XMLList. If you want to replace it another value try the following:
[Bindable] //Notice this metadata
public var treeData:XMLList = new XMLList(MyString);
public function updateTreeData():void {
var text:String = ...; //loading text
treeData = new XMLList(text);
}
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.
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.
Sort of a Flex newbie here so bear with me. I've got a DataGrid defined as follows:
<mx:Script>
...
private function getColumns(names:ArrayCollection):Array {
var ret:Array = new Array();
for each (var name:String in names) {
var column:DataGridColumn = new DataGridColumn(name);
ret.push(column);
}
return ret;
}
</mx:Script>
<mx:DataGrid id="grid" width="100%" height="100%" paddingTop="0"
columns="{getColumns(_dataSetLoader.columnNames)}"
horizontalScrollPolicy="auto" labelFunction="labelFunction"
dataProvider="{_dataSetLoader.data}"
/>
...where _dataSetLoader is an instance of an object that looks like:
[Bindable]
public class DataSetLoader extends EventDispatcher {
...
private var _data:ArrayCollection = new ArrayCollection();
private var _columnNames:ArrayCollection = new ArrayCollection();
...
public function reset():void {
_status = NOTLOADED;
_data.removeAll();
_columnNames.removeAll();
}
...
When reset() is called on the dataSetLoader instance, the DataGrid empties the data in the cells, as expected, but leaves the column names, even though reset() calls _columnNames.removeAll(). Shouldn't the change in the collection trigger a change in the DataGrid?
Your data is properly bound because you refer directly to the variable as dataProvider.
For the columns, you refer to a function call.
Can you assign the values of _dataSetLoader.columnNames to a bindable ArrayCollection instead?
Then use that ArrayCollection as columns.
Well there are various alternatives or work arounds. It depends on what exactly is your requirement.
Below is what you can do with your Datagrid component
If you already know the column names i.e In your UI Interface the column names do not change. You might wanna hard code them instead of supplying dynamically.
If the column name changes with the array collection or the dataprovider, I suggest you remove the column property of your datagrid and let the default column names be displayed.
You can also add Columns at run time depending on inputs provided by drop down or check boxes or some other conditions.
Check out Flex Documentation for more info.
What I am trying to accomplish to to get financial data in my Flex Datagrid to be color-coded--green if it's positive; red if it's negative. This would be fairly straightforward if the column I want colored was part of the dataProvider. Instead, I am calculating it based on two other columns that are part of the dataProvider. That would still be fairly straightforward because I could just calculate it again in the ItemRenderer, but another part of the calculation is based on the value of a textBox. So, what I think I need to be able to do is send the value of the textBox to the custom ItemRenderer, but since that value is stored in the main MXML Application, I don't know how to access it. Sending it as a parameter seems like the best way, but perhaps there's another.
Here is the current code for my ItemRenderer:
package {
import mx.controls.Label;
import mx.controls.listClasses.*;
public class PriceLabel extends Label {
private const POSITIVE_COLOR:uint = 0x458B00 // Green
private const NEGATIVE_COLOR:uint = 0xFF0000; // Red
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
/* Set the font color based on the item price. */
setStyle("color", (data.AvailableFunding >= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
}
}
(data.AvailableFunding doesn't exist)
So does anyone know how I would go about accomplishing this?
You may want to look into ClassFactory from the Flex APIs:
This allows you to set a prototype Object with arbitrary types / values each of which will be passed to the item renderer. From the sample:
var productRenderer:ClassFactory = new ClassFactory(ProductRenderer);
productRenderer.properties = { showProductImage: true };
myList.itemRenderer = productRenderer;
The above code assumed that "ProductRenderer" has a public property called "showProductImage" which will be set with a value of "true."
Ah, so I knew about outerDocument but not parentDocument. I was able to just use parentDocument.*whatever I want from the main App and I can access it as long as it's public.
Example:
setStyle("color", (parentDocument.availableFunding >= 0) ? POSITIVE_COLOR : NEGATIVE_COLOR);
Sweet! :)
You can access the value of the TextBox directly, if you need to, by using the static Application.application object, which is accessible from anywhere in your application.
For example, if you wanted the renderers to be notified when the value of the TextInput control changes, you could do something like this (from within your ItemRenderer, and where myTextInput is the ID of the control defined in your main MXML class):
<mx:Script>
<![CDATA[
import mx.core.Application;
private function creationCompleteHandler(event:Event):void
{
Application.application.myTextInput.addEventListener(TextEvent.TEXT_INPUT, handleTextInput, false, 0, true);
}
private function handleTextInput(event:TextEvent):void
{
if (event.currentTarget.text == "some special value")
{
// Take some action...
}
}
]]>
</mx:Script>
With this approach, each item-renderer object will be notified when the TextInput's text property changes, and you can take appropriate action based on the value of the control at that time. Notice as well that I've set the useWeakReference argument to true in this case, to make sure the listener assignments don't interfere unintentionally with garbage collection. Hope it helps!
There's another technique, which, while it initially feels a little hacky is perhaps less cumbersome and cleaner in actual use.
It involves the little-observed fact that an event dispatch is, of course, synchronous and the event object can be treated as a value object populated by any event handler.
i.e. the ItemRenderer can do something like:
...
var questionEvt:DynamicEvent = new DynamicEvent('answerMeThis', true, true);
if (dispatchEvent(questionEvt))
{
if (questionEvent.answer == "some value")
....
With a corresponding handler somewhere up the view hierarchy above the renderer that has a listener on the event and does something like:
function handleAnswerMeThis(event:DynamicEvent):void
{
event.answer = "another value";
event.dataHelper = new DataHelperThingy();
}
etc.
It need not be a DynamicEvent - I'm just using that for lazy illustrative purposes.
I vote up for cliff.meyers' answer.
Here's another example on setting the properties of an itemRenderer from MXML by building a function that wraps a ClassFactory around the itemRenderer class and that injects the necessary properties.
The static function:
public static function createRendererWithProperties(renderer:Class,
properties:Object ):IFactory {
var factory:ClassFactory = new ClassFactory(renderer);
factory.properties = properties;
return factory;
}
A simple example that adds a Tooltip to each item in a list:
<mx:List dataProvider="{['Foo', 'Bar']}" itemRenderer="{createRendererWithProperties(Label, {toolTip: 'Hello'})}"/>
Reference:
http://cookbooks.adobe.com/post_Setting_the_properties_of_an_itemRenderer_from_MXM-5762.html
You use outerDocument property. Please see the fx:Component reference.
You could create an 'AvailableFunding' static variable in the ItemRenderer and then set it in the parent document.
public class PriceLabel extends Label {
public static var availableFunding:int;
...
...
SetStyle("color", (PriceLabel.availableFunding >= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
}
In your parent document, set it when your text box gets updated
PriceLabel.availableFunding = textBox.text;
Obviously it'll be the same value for every ItemRenderer but it looks like that might be what you're doing anyway.
I like to override the set data function of the item renderer to change the renderer when the data provider changes as shown here
When you override the function you could cast the object to your object to make the availableFunding property available.
To access the text box you could try creating a public property and binding the property to the text box in the mxml file:
public var textVar:String;
<mx:itemRenderer>
<mx:Component>
<customrenderer textVar="{txtBox.text}" />
</mx:Component>
</mx:itemRenderer>
Nice ClassFactory Example here
See this example:
itemRenderer="{UIUtils.createRenderer(TextBox,{iconSrc:IconRepository.linechart,headerColor:0xB7D034,subHeaderColor:0xE3007F,textColor:0x75757D})}"