I have two scripts in a View, one script is inside a Component.
In the Component script I need to add numbers from data as they are added to a list and then display the total in a label placed in the View.
If I declare the variable in the first script, the Component script can't see it and if I declare it in the Component Script the label can't see it.
How will I declare it so that everyone in the view can see it?
Thanks,
Kim
Here is the code, the problem is where should I place the var MyTotal so it can be used anywhere in the view:
<s:view
<fx:Script>
<![CDATA[
//if I place it here the next CDATA inside IconItemRender can't see it.
private static var MyTotal:Number=0;
]]>
</fx:Script>
<fx:Declarations>
<s:CurrencyFormatter id="usdFormatter" useCurrencySymbol="true"/>
</fx:Declarations>
<s:itemRenderer>
<fx:Component>
<s:IconItemRender ..............>
<fx:Script>
<![CDATA[
//if I place it here the Label "TotalAmountLb" can't see it.
// and it get reset to 0 everytime I call the function getInvoiceAmount.
private static var MyTotal:Number=0;
private function getInvoiceAmount(item:Object):String
{
MyTotal = MyTotal + Number(item.Amount);
}
]]>
</fx:Script>
</s:IconItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
<s:Label id="TotalAmountLb" text="{usdFormatter.format(MyTotal)}"/>
</s:view>
Just try this: Declare MyTotal in first script. And in component script just try to access with outerDocument.MyTotal
Found the solution, I had to add [Bindable] before the declaration.
Related
I want to add a button which when clicked adds another text field. I am using adobe flash builder to write the application therefore it needs to be in MXML or actionscript. Any ideas as to how this could be done?
The eventhandler button currently points to this code, however after the first addition of the textbox, it stops and doesn't add any more. How do I make a loop to keep adding textfields for every time the button is clicked?
<fx:Script>
<![CDATA[
protected function tableID(event:MouseEvent):void
{
var name:TextInput = new TextInput;
addElement(name);
name.move(50, 200);
}
]]>
</fx:Script>
MXML:
<s:Button id="addBtn" x="175" y="450" label="+" click="tableID(event)" />
Just use a boolean variable for the text field. Set the variable for includeInLayout & visible for text field. On clicking the button set the condition for the boolean variable to be true or false. I think this will help you out.
You can try this way:
<fx:Script>
<![CDATA[
import mx.controls.TextInput;
protected function bt_clickHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
var item:TextInput = new TextInput();
item.width = 50;
_parent.addElement(item);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:Tile id="_parent" width="100%" height="100%">
<s:Button id="bt" label="+" click="bt_clickHandler(event)"/>
</mx:Tile>
Is it possible to access a declared component as an IFactory within an MXML class? I've used this style of declaring factories many times for Skin Parts, but I've never figured out how to access those factories from within the MXML.
Here's an example of what I would expect to work:
<fx:Declarations>
<fx:Component id="labelDisplay">
<s:Label fontSize="12" fontWeight="bold"/>
</fx:Component>
</fx:Declarations>
<fx:Script>
<![CDATA[
override protected function createChildren():void
{
super.createChildren();
var label1:Label = labelDisplay.newInstance();
addElement(label1);
var label2:Label = labelDisplay.newInstance();
addElement(label2);
var label3:Label = labelDisplay.newInstance();
addElement(label3);
}
]]>
</fx:Script>
* edit *
The reason I was hoping the above code would work is based on the way dynamic Skin Parts are handled in the Spark skinning architecture. If the above code were a part of an MXML skin class, then in my host component, I could have the following.
[SkinPart(required="true",type="spark.controls.Label")]
public var labelDisplay:IFactory;
In the Spark skinning architecture, at what point does the <fx:Component> turn into an IFactory?
I dug into Flex's SkinnableComponent to find the solution based on how they tie an MXML Skin to an AS HostComponent. Apparently, even though "labelDisplay" doesn't show up in FlashBuilder's autocomplete as a concrete class member, you can still reference it as a dynamic property. I've modified my original example here:
<fx:Declarations>
<fx:Component id="labelDisplay">
<s:Label fontSize="12" fontWeight="bold"/>
</fx:Component>
</fx:Declarations>
<fx:Script>
<![CDATA[
override protected function createChildren():void
{
super.createChildren();
var labelFactory:ClassFactory = this["labelDisplay"];
var label1:Label = labelFactory.newInstance();
addElement(label1);
var label2:Label = labelFactory.newInstance();
addElement(label2);
var label3:Label = labelFactory.newInstance();
addElement(label3);
}
]]>
</fx:Script>
The problem I see is that you didn't create an iFactory, so won't be able to access iFactory methods.
I would recommend you drop into ActionScript and actually create an iFactory if you need one. This uses the ClassFactory class
protected var labelDisplay: iFactory = new ClassFactory(spark.controls.Label);
Then your createChildren code should work as is.
I have a code in MXML and ActionScript that I found in a Flex manual. The problem is with "val" variable that should be passed to the updateMyString() function calling statement as a parameter but it doesn't happen in the code. Why is that?
<?xml version="1.0"?>
<!-- binding/BindSetterAS.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.binding.utils.*;
import mx.events.FlexEvent;
// Method called when myTI.text changes.
public function updateMyString(val:String):void {
myTA.text = val.toUpperCase();
}
<!-- Event listener to configure binding. -->
public function mySetterBinding(event:FlexEvent):void {
var watcherSetter:ChangeWatcher =
BindingUtils.bindSetter(updateMyString, myTI, "text");
}
]]>
</fx:Script>
<s:Label text="Bind Setter using setter method"/>
<s:TextInput id="myTI"
text="Hello Setter" />
<s:TextArea id="myTA"
initialize="mySetterBinding(event);"/>
</s:Application>
The documentation says that BindingUtils.bindSetter expects a setter function with one argument:
setter:Function — Setter method to invoke with an argument of the current value of chain when that value changes
In this case the val parameter will be set to the current value of chain. You can read about it here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/binding/utils/BindingUtils.html#bindSetter
Hope this helps!
var watcherSetter:ChangeWatcher = BindingUtils.bindSetter(updateMyString, myTI, "text");
the function is like this:
bindSetter(functionToCall, objectToBind, PropertyToBind)
So, you are actually binding myTI.text. This is passed as an argument to functionToCall, i.e. updateMyString. Got it?
I am pretty new to flex, specially to mxml part. There are some confusing things for me.
For example I have custom component file Abc.mxml with code:
<s:Group>
//Lots of code
<s:Button id="someId" /*code*/ />
</s:Group>
And then I have Xyz.mxml which is also custom component file.
<s:Group>
<fx:Scrip>
//something happens here, for example some other button click or whatever
</fx:Script>
//code
<comp:Abc />
</s:Group>
So question is how do I access that button's properties. I want when something happens in Xyz file, button's (someId) visibility to become false. If Abc.mxml was AS class file then it would be easy, just make object etc., but how to get if it's mxml file, I have no idea.
There's no big difference between mxml and as. When you write Xyz.mxml:
<s:Group>
<fx:Scrip>
//something happens here, for example some other button click or whatever
</fx:Script>
//code
<comp:Abc />
</s:Group>
...you just specify class Xyz derived from Group. Mxml - is just markup language which makes building interfaces easier. During compiling mxml files are transformed to pure AS, so there's nothing (on a large scale) you can do in mxml which you can't in AS and vice-versa.
ID property in mxml is similar to instance name in AS, i.e. it will be converted to public property in your calss.
Answer to your question.
You can write public function in Abc and call it in Xyz.
Abc.mxml:
<s:Group>
<fx:Script>
<![CDATA[
public function doSomething():void
{
someId.enabled = false;
}
]]>
</fx:Script>
<s:Button id="someId"/>
</s:Group>
Xyz.mxml:
<s:Group>
<fx:Script>
<![CDATA[
private function somethingHappened():void
{
abcComponent.doSomething();
}
]]>
</fx:Script>
//code
<comp:Abc id="abcComponent"/>
</s:Group>
In somethingHappened function you can access Button abcComponent.someId directrly, but I strongly reccommend not to do this, since it breaks encapsulation and makes your classes more cohesive (and so on).
I'm trying componentize one of the pieces of UI in an AIR application that I'm developing in Flex. In this example, I want to display file information on a single line (which has an icon, some text/link and the size).
My code looks like this (component is called FileDisplay):
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public function set iconType(source:String):void {
this.ficon.source = source;
}
public function set fileName(name:String):void {
this.fname.htmlText = name;
}
public function set fileSize(size:String):void {
this.fsize.text = size;
}
]]>
</mx:Script>
<mx:Image id="ficon" />
<mx:Label id="fname" left="20" right="30" text="Filename" />
<mx:Label id="fsize" right="0" text="0 K" />
</mx:Canvas>
When I'm using this component in my main application, the actionscript looks like:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
this.file_list.addChild(fd);
}
However, when I do this, I get an error: Error #1009: Cannot access a property or method of a null object reference. This is because the child components of the FileDisplay are null (or at least they show up that way in the debugger).
Does anyone know if there's a way around this? Am I supposed to be waiting for events indicating the child components were created? Is there a more common pattern that solves this problem?
For now I can manually do everything in ActionScript in my main app (create a Canvas and add children to it) but I would appreciate any insight on how to separate the code more cleanly.
Bindable to the rescue:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
public var iconType:String;
[Bindable]
public var fileName:String = "Filename";
[Bindable]
public var fileSize:String = "0 K";
]]>
</mx:Script>
<mx:Image id="ficon" source="{iconType}"/>
<mx:Label id="fname" left="20" right="30" text="{fileName}" />
<mx:Label id="fsize" right="0" text="{fileSize}" />
</mx:Canvas>
the values will be automatically updated when the components are created.
The subcomponents haven't been loaded yet.
Read this: http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html#203434.
Then, when like me, you don't understand it (and it's not reliable), listen for the FlexEvent.CREATION_COMPLETE within FileDisplay, and apply your child component properties there.
Or better yet, create the three children programmatically in the "createChildren" function, and apply the settings there.
Both of these methods assume that you're setting filename, icontype, and filesize as local members before applying them to the children components, which you should be doing regardless.
What is the parent component that holds the FileDisplay component? If you're sure that the error is coming from the fact that the child components of FileDisplay aren't being instantiated then you might want to look at the creationPolicy attribute and make sure it's set to ContainerCreationPolicy.ALL on that parent component.
=Ryan
In addition to setting the CreationPolicy to all, you need to add the DisplayObject to the stage via addChild. The children of FileDisplay are not created until you add it is added to the stage. So do:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
this.file_list.addChild(fd);
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
}