passing data from view to custom component in flex - apache-flex

I have extended the SkinnablePopUpContainer to make a popup window in mobile application. But i don't know how to pass a variable defined in the main view to this component. My code looks like the following
<fx:Declarations>
<fx:Component className="Alert">
<s:SkinnablePopUpContainer>
<s:Button label="OK" click="close()"/>
<s:SkinnablePopUpContainer>
<fx:Script>
<![CDATA[
private function setMetaDataXML(metaDataXML:XML):void{
var temp = metaDataXML;
}
]]>
</fx:Script>
</fx:Component>
</fx:Declarations>
--- main view continues
and here is how I call the component on a button click from the main view:
click="(new AlertMsg()).open(this, false)"
now i just want to call setMetaDataXML from the main view and pass the value. How can I achieve that? Thank you

I would recommend to create your component in a separate mxml file or even better in a pure AS3 based component. To do what you want to do, you could create a handler function in your Script area for that click event, and in there assign the instance of the component and then call any method before calling open, like:
click = "clickHandler()"
----- inside your script
function clickHandler : void {
var a : AlertMsg = new AlertMsg();
a.setMetaDataXML("foo");
a.open(this,false);
}
I would also recommend you to declare implicit setters and getters the AS3 way

Related

Spark List with Buttons

I have a Spark List with a data provider consisting of a list of filled out form applications. What is the best way to add a button to each List Item (form application)? This button will be named Open and will navigate to the specified form application.
Thanks in advance for any advice!
This is similar to what #www.Flextras.com said, so I'm not going to repeat that. However, I'll add an example and one or two things.
Your custom ItemRenderer might look like this:
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
import mx.events.ItemClickEvent;
private function requestForm():void {
var event:ItemClickEvent = new ItemClickEvent(ItemClickEvent.ITEM_CLICK);
event.index = itemIndex;
event.item = data;
owner.dispatchEvent(event);
}
]]>
</fx:Script>
<s:Label id="labelDisplay" verticalCenter="0" />
<s:Button right="0" label="open" verticalCenter="0" click="requestForm()" />
</s:ItemRenderer>
Two things that differ from Flextras' answer:
I use the built-in ItemClickEvent instead of a custom event > less
coding
I dispatch the event on the owner of the ItemRenderer, which
is in fact the List that contains this ItemRenderer. Because of this,
you don't need to bubble the Event.
Now to open the form when the Button is clicked, do something like this:
myList.addEventListener(ItemClickEvent.ITEM_CLICK, openForm);
private function openForm(event:ItemClickEvent):void {
trace("open " + event.item.toString());
}
Use a custom itemRenderer which displays the button along w/ your itemRenderer data (form application).
When the button is clicked; dispatch a custom event which bubbles. You may have to include some identifier for the form application this button click represents.
Listen to the event on the list class using the addEventListener() method. You can't use MXML since you'll be using a custom event undefined in the List's default metadata.
In your listener, perform the relevant UI changes to display your form application.

How to dynamically access all text inputs within a custom component in flex 4

I have a custom component which contains many promptingTextInput controls.
When i click a button, I want the text field of all the promptingTextInputs to become blank.
(Note: I have around 60 promptingTextInputs)
How do i access the controls dynamically ? I am looking for a flex equivalent of $('input[type=text]') (like in jquery).
As for me the best way to obtain it is to follow data driven design. Create data object (I mean a dedicated class) with dedicated fields (of String type) for every your TextInput. Then bind your text inputs with fields using two way binding. Then you can clear all text inputs by recreating object. I'll illustrate it with the following pseudo code. Lets create our data object aka VO:
[Bindable]
public class Person
{
public var firstName:String;
public var lastName:String;
}
Then our MXML class:
<fx:Script>
<![CDATA[
[Bindable]
private var person:Person = new Person();
]]>
</fx:Script>
<s:TextInput text="#{person.firstName}" />
<s:TextInput text="#{person.lastName}" />
<s:Button label="Clear" click="person = new Person()" />
It seems it will be better in design point of view and much more simple. And you can use composition within your data object and still take advantage of binding.

A question on capturing the button click event in ANOTHER mxml file

this seems to be an interesting question to be discovered in Flex.
I registered a very simple button event listener in A.mxml:
<mx:Script><![CDATA[
import mx.controls.Alert;
public function Handler():void
{
Alert.show('click event occured');
}
]]></mx:Script>
<mx:Button label="{resourceManager.getString('resources', 'button.startLab')}"
id="nextStepButton" click="Handler()" />
It works fine when clicking the button everytime.
Now I want to have something interesting,that is,I want to capture this buttonClick Event in another mxml file,say B.mxml and do something in B.mxml instead of A.
I am bit stuck on this,hope you could give me some hint and help,thanks a lot.
There are a number of approaches to this problem. The simplest (and least object-oriented) is to have A be aware of B, or vice versa. In that case you can just add a listener. In B you could say a.nextStepButton.addEventListener(MouseEvent.CLICK, myHandler), or in A you could do this.nextStepButton.addEventListener(MouseEvent.CLICK, b.myHandler). (When one component is instantiated, you have to set a reference to it on the other component.)
One step better would be to dispatch a custom event that bubbles, with one of the components still aware of the other. In B: a.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler), or in A: b.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler).
Taking it further, you could just let the event bubble to the top (the SystemManager) and add your listener to the SystemManager. This way B is not aware of A at all. In B: this.systemManager.addEventListener(CustomNavigationEvent.NEXT_CLICK, myHandler).
Taking it even further, you can implement your own version of an event broadcaster, which is just a third object that is accessible by any component, usually implemented as a singleton, that takes listener registrations and accepts event dispatches, then broadcasts that event to registered listeners.
Hope that helps.
EDIT: Here's some code for doing it the first way:
In A.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" creationComplete="onCreationComplete(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
public var b:B;
private function onCreationComplete(e:FlexEvent):void {
// Note that you have to have a public onClick handler in B
this.myButton.addEventListener(MouseEvent.CLICK, b.onClick);
}
]]>
</fx:Script>
<s:Button id="myButton"/>
</s:Group>
You need to make A aware of B in the container that declares instances of both A and B:
MXML:
<mypackage:A id="aComponent" b="bComponent"/>
<mypackage:B id="bComponent"/>
ActionScript equivalent:
var aComponent:A = new A();
var bComponent:B = new B();
aComponent.b = bComponent;

Initialize properties of custom component before creating children in flex

Say I have the following custom component:
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
[Bindable]
public var prop:String;
private function formatProp() : String {
return "Hello, " + prop;
}
]]>
</fx:Script>
<s:Label text="User: {prop}"/>
<s:Label text="Greeting: {formatProp()}"/>
</s:Group>
If I add it to my application like this:
<local:MyComponent prop="Hello"/>
The result looks like:
User: Mark
Greeting: Hello, null
It seems Flex is setting prop on my custom component after it has already initialized the child labels, so it's reliant on the property changed event to set the user label.
Is there an elegant way to make Flex wait for all of my component's properties to be set before initially evaluating bindings?
Note: I realize the formatProp function is trivial and could be included inline, but this is just a simplified example.
The "elegant way" would be to actually provide data binding, so that you can change your property also afterwards. Your initial idea looked good, working with the answer provided by Cornel. I just wanted to mention this as your actual question sounded more like that you know your data binding is not working and you just wanted to postpone the initial setting of the variable.
Btw, should you plan to create custom components in Actionscript (instead of mxml) you'll face the opposite problem: properties are set before you had a chance to actually create your children, so you may need to buffer them if they actually should influence some childs properties.
it is not related to component livecycle, more to binding rules. Your function "formatProp" should recieve the parameter "prop" as a parameter in order to be called when the prop is changed. Try this code:
private function formatProp(props:String) : String {
return "Hello, " + props;
}
<s:Label text="Greeting: {formatProp(prop)}"/>

Using a composite MXML component from ActionScript

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);
}

Resources