Access data in custom component in Flex - apache-flex

I am new to Flex so pardon me if my questions are quite basic. I have searched a lot before posting here, might be I was not looking in the right direction. Please redirect me to the path that leads to the solution of the problem. I really appreciate any help that I can get.
I'm following this video tutorial. (I was creating Mobile Project not a simple Flex project like in video)
http://www.gotoandlearn.com/play.php?id=100
All was going fine, until the tutor wanted to add custom component in the application. He added the HBox which I couldn't find in Flash Builder 4.6 so I added HGroup instead in my new component. Now when I want to use the data that was fetched in the parent component in custom component it gives me error. Here is the code and their file names.
File: SearchHomeView.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Twitter Search">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<s:HTTPService result="onResult(event)" id="service" url="http://search.twitter.com/search.atom?q=adobe">
</s:HTTPService>
</fx:Declarations>
<fx:Script>
<![CDATA[
import flash.utils.flash_proxy;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
[Bindable]
private var ac:ArrayCollection;
private function onResult(event:ResultEvent):void
{
ac = event.result.feed.entry as ArrayCollection;
trace(data);
trace(ac);
}
private function doSearch(event:MouseEvent):void
{
//service.url = "http://search.twitter.com/search.atom?q=" + tearch.text;
service.url = "http://search.twitter.com/search.atom?q=adobe";
service.send();
}
]]>
</fx:Script>
<s:TextInput x="25" y="26" width="146" id="tearch"/>
<s:Button x="224" y="26" height="33" label="Search" click="doSearch(event)" />
<s:List dataProvider="{ac}" itemRenderer="tweet" x="25" y="92" width="274" height="278"></s:List>
</s:View>
File: tweet.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:HGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" width="400" height="300">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Image width="50" height="50" source="{parentDocument.data.link.getItemAt('1').href}">
</s:Image>
<s:TextBase width="100%" text="">
</s:TextBase>
</s:HGroup>
When I use source to be source="{parentDocument.data.link.getItemAt('1').href} ... it removes the error, but displays nothing on the resulting app.
When I use source to besource="{data.link[1].href} ... it gives the error,
Multiple markers at this line:
-1120: Access of undefined property data.
-parentDocument
What needs to be done to use the item renderer right in custom component ? Please tell me the solution to it... I'm stuck on it for quite a few time.

Your component Tweet.mxml should extend an ItemRenderer.
In Flex 3 many components could be used as item renderers, that old (Flex 3) HBox component you saw in the video works as an item renderer b/c it has a data property.
As part of Flex 4's "pay as you go" approach, the container classes (Group, HGroup, etc) do not support being used directly as item renderers. As a result HGroup doesn't have a data property.
Try making Tweet.mxml look something like this:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" width="400" height="300">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:layout>
<s:HorizontalLayout />
</s:layout>
<!-- Typically, the dataProvider of your List is an array of objects of the same
type. So each item renderer that is on screen has it's data property populated
with one of these items. You can then craft an MXML document like this that
uses the properties of your array element. You're going to need to use the
right expression that finds the image source in the data item. If you really
want to be rendering the array data.link, pass that data w/an ArrayCollection
to the list instead ... it's hard to advise here w/out knowing what your data
looks like. -->
<s:Image width="50" height="50" source="{data.link.getItemAt('1').href}" />
<s:StyleableTextField width="100%" text="" />
</s:ItemRenderer>
The changes I'm making are:
extend ItemRenderer
replace HGroup's layout by using the HorizontalLayout in the renderer
using the data property of the renderer for the source of the image (use the data property to populate all of the dynamic parts of the renderer (like the text field)
using a StyleableTextField, optimized text for mobile

In your onResult event handler - be careful to check that you are in fact assigning all items into the arraycollection - If feed.entry is not explicitly an ArrayCollection you will need to iterate the list (assuming this an xmllist because it looks like an RSS feed)...
So instead:
protected function onResult(event:ResultEvent):void
{
if (!ac) ac = new ArrayCollection();
for each (var entry:XML in event.result.feed..entry)
{
ac.addItem(entry);
}
}
As for the ItemRenderer, you have a couple of different options...
ItemRenderer - as suggested by #Sunil, this is the 'base' class of renderers to use in spark based lists.
DataGroup - this is similar to a Group where you specify a layout, but it creates "renderers" - anything with a 'data' property using a dataprovider, the key to this one though is there is no virtualization it just creates all of them.
It gets more complicated than this when you switch to a DataGrid...

Related

What is the MXML syntax to assign properties of subcomponents in custom MXML Components?

I am working on a custom Flex 4 component which is an aggregation of two existing flex components. I would like to be able to specify my own custom properties for the component as well as access the existing public subcomponent properties via MXML. For instance I might want to adjust the font color or style for the label and text input.
A toy component which aggregates both a label and a text input:
<?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"
>
<fx:Script>
<![CDATA[
[Bindable] public var prompt:String = "default prompt";
[Bindable] public var input:String = "default inpput";
]]>
</fx:Script>
<s:VGroup>
<s:Label id="cLabel" text="{prompt}" />
<s:TextInput id="cTextInput" text="{input}" />
</s:VGroup>
</s:Group>
Then in my main application I would like to access the public interfaces of the sub-component via mxml without re-writing a pass-through binding for every one. Something like:
...
<local:myInput prompt="name" input="please enter name">
<local:cLabel color="0xffffff" />
<local:CTextInput fontStyle="bold" />
</local:myInput>
In actionscript one can do this easily for all public properties:
myInput.cLabel.color = "0xffffff";
But I am stumped on the syntax for MXML. This seems like it should be easy, but I have not found the answer yet. Any help greatly appreciated.
You can't daisy chain down an display hierarchy w/ the MXML tag/value. You can do it in ActionScript, as you specified, but even that would probably be considered a bad practice.
I'll point out that color on the Label and fontStyle on the TextInput are not properties. They are styles So, the code you have:
myInput.cLabel.color = "0xffffff";
Would most likely throw an error because color is not a property. You'd have to use code like this:
myInput.cLabel.setStyle('color',"0xffffff");
However, since styles are usually inherited by the children; I suspect at the top level component, you can set the style and it would immediately trickle through to the children automatically. So, you should just be able to do:
myInput.setStyle('color',"0xffffff");
Or in MXML:
<local:myInput prompt="name" input="please enter name" color="0xffffff" fontStyle="bold" >
</local:myInput>
And it should trickle on down. Things can get trickier if you want to set styles individually on child components.
But, back to your original question w/ regards to properties. To keep a component encapsulated, you should create properties that set on the children. Something like this:
private var _property : String;
public function get property():String{
return _property;
}
public function set property(value:String){
_property = value;
myChildComp.property = value;
}
It can suck if you need to do this for a lot of properties. If encapsulation of this component isn't a priority; just set them in ActionScript.

How to check the state of a Radio Button in Flex 3?

I'm working on a template for dynamic questionnaires in Flex. More detailed description in my previous thread HERE
To create the questionnaire, I use nested repeaters - one for the questions and one for the answers (as their amount may vary).
<mx:Repeater id="r" dataProvider="{questions}">
<mx:Label text="{r.currentItem.question}" width="200"/>
<mx:Repeater id="r2" dataProvider="{r.currentItem.answers}">
<mx:RadioButton label="{r2.currentItem.text}" width="200"
click="answerHandler(event, event.currentTarget.getRepeaterItem())"/>
</mx:Repeater>
</mx:Repeater>
To understand my data providers, it's probably best to check the answer for my previous thread - easier than if I try to explain it here.
The question here is... As you can see, I created click event handler for each radio button and my plan was to do playerScore++ every time the user chose correctly (which can be achieved by checking the Boolean property "correct" of sent RepeaterItem).
However, I see now that even if the button is selected already, I can still click on it more times, and even though it's not changing anything in the view, it increments the score every time.. I would also have to handle situation in which the user changes his mind (I could give + points for each good answer and - points for wrong, but this would mean, that if the user chose more wrong answers, my score will be negative and I don't want it).
So it would be way way easier to just have a Submit button and check the states of all my buttons and if they are correct only after the user clicks "submit". Is it possible?
I recommend you to refer to the following sample and add RadioButtonGroup to your repeater groups and listen to change events instead of click. You can listen change event right in RadioButtonGroup and check if (radioGroup.selectedValue.correct) for correctness of new selection. See corresponding documentation.
To have possibility to assign radiogroups unique name you have to extract inner repeater with answers into separate component. Breaking your application into smaller components can make your application more clear and readable. You can treat every MXML file as a class (as in OOP) but in declarative form. And to tell the true every MXML component is a class which inherited from root-node-class.
Lets look at the code.
First, our inner component which serves answers:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[Event(name="rightAnswerEvent", type="AnswerEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var answers:ArrayCollection;
protected function answerGroup_changeHandler(event:Event):void
{
if (answerGroup.selectedValue.correct)
dispatchEvent(new AnswerEvent(AnswerEvent.RIGHT_ANSWER_EVENT));
}
]]>
</mx:Script>
<mx:RadioButtonGroup change="answerGroup_changeHandler(event)" id="answerGroup" />
<mx:Repeater dataProvider="{answers}" id="answersRepeater">
<mx:RadioButton group="{answerGroup}" label="{answersRepeater.currentItem.text}"
value="{answersRepeater.currentItem}" />
</mx:Repeater>
</mx:VBox>
It gets answers collection as input and fires our custom event to inform some components about right answer (see Metadata tag).
Our custom event is pretty simple an looks like the following:
package
{
import flash.events.Event;
public class AnswerEvent extends Event
{
public static const RIGHT_ANSWER_EVENT:String = "rightAnswerEvent";
public function AnswerEvent(type:String)
{
super(type);
}
}
}
So now our top level component:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="vertical" xmlns:local="*"
xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var questions:ArrayCollection = new ArrayCollection();
[Bindable]
private var scoreCounter:int;
]]>
</mx:Script>
<mx:Label text="{'Score ' + scoreCounter}" />
<mx:Repeater dataProvider="{questions}" id="questionRepeater">
<mx:Label text="{questionRepeater.currentItem.question}" />
<local:AnswerGroup answers="{questionRepeater.currentItem.answers}" rightAnswerEvent="scoreCounter++" />
</mx:Repeater>
</mx:Application>
I omitted initialization code to populate our Question and Answer domain objects with data from XML (see previous thread).
So now we have compact modularized code where every part solves its own task.
Hope this helps!

More than one titleWindow in a Flex Application

I'm a GIS Analyst that was moved to an Analyst Programmer position. This has been a hard transition for me as I don't have much of a programming background, but I was thrown into it.
I'm working on a Flex app inside a jsp page. Essentially it is a grid 3x2 that has images and text. What I am trying to do is have more than one titleWindow reference in this page, so that when an image is clicked a titleWindow is opened. (If anyone has a better idea, especially if it has to do with a hover I am very open to that!) Currently I have it working for one image. However, when I try to add a second function it errors on me. "Error 1021: Duplicate function definition" Below is the entire code for the main page that calls up the titleWindow. The code below is what gives the Error 1021.
<?xml version="1.0" encoding="utf-8"?>
<![CDATA[
import flash.geom.Point;
import mx.containers.TitleWindow;
import mx.core.IFlexDisplayObject;
import mx.managers.PopUpManager;
import windows.SimplePopupWindow;
private var point1:Point = new Point();
private function showWindow():void {
var login:SimpleTitleWindowExample=SimpleTitleWindowExample(PopUpManager.createPopUp( this, SimpleTitleWindowExample , true));
point1.x=131;
point1.y=119;
point1=roadStatus.localToGlobal(point1);
}
private var point2:Point = new Point();
private function showWindow():void {
var login:SimpleTitleWindowExampleFlood=SimpleTitleWindowExampleFlood(PopUpManager.createPopUp( this, SimpleTitleWindowExampleFlood , true));
point2.x=289;
point2.y=119;
point2=floodplain.localToGlobal(point2);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:BorderContainer x="10" y="0" width="750" height="600" backgroundColor="#BBB082" backgroundAlpha="1.0" cornerRadius="20" borderColor="#E8DBA7">
<s:Panel x="10" y="10" width="728" height="578" cornerRadius="20" chromeColor="#983D3A" borderColor="#F1EFE7" backgroundColor="#BBB082">
<mx:Image x="131" y="119" width="150" height="115" source="file://GIS Map Portal/images/Map Images/SJCRoadStatus2_small.jpg" id="roadStatus" click="showWindow();"/>
<mx:Image x="289" y="119" width="150" height="115" source="file://GIS Map Portal/images/Map Images/SJCRoadStatus_small.jpg" id="floodplain" click="showWindow();"/>
<mx:Image x="447" y="119" width="150" height="115" source="file://GIS Map Portal/images/Map Images/SJCRoadStatus2_small.jpg"/>
<s:Label x="131" y="242" text="SJC Road Status"/>
<s:Label x="289" y="242" text="SJC Floodplain"/>
<s:Label x="447" y="242" text="Assessor's Parcels"/>
<mx:Image x="131" y="262" width="150" height="115" source="file://GIS Map Portal/images/Map Images/SJCRoadStatus_small.jpg"/>
<mx:Image x="289" y="262" width="149" height="115" source="file://GIS Map Portal/images/Map Images/SJCRoadStatus2_small.jpg"/>
<mx:Image x="446" y="262" width="151" height="115" source="file://GIS Map Portal/images/Map Images/SJCRoadStatus_small.jpg"/>
<s:Label x="131" y="385" text="Label"/>
<s:Label x="289" y="385" text="Label"/>
<s:Label x="446" y="385" text="Label"/>
<s:Label x="229" y="24" text="San Juan County Web Maps" fontFamily="Calvin and Hobbes" fontSize="25"/>
</s:Panel>
</s:BorderContainer>
Below is the titleWindow code. This code works so far!
<?xml version="1.0" encoding="utf-8"?>
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
import mx.controls.Text;
// A reference to the TextInput control in which to put the result.
public var loginName:Text;
// Event handler for the OK button.
private function returnName():void {
//loginName.text="Name entered: " + userName.text;
PopUpManager.removePopUp(this);
}
]]>
</mx:Script>
<mx:HBox width="323" height="147" borderColor="#E8DBA7" dropShadowVisible="true">
<mx:Text text="The San Juan County GIS Department maintains aninteractive web map dedicated for researching county roads, but also includes city limits, lakes and rivers, and other geographic data.
" width="319" height="76" textAlign="center" color="#FFFFFF"/>
</mx:HBox>
<mx:HBox>
<mx:Button label="Go" click="navigateToURL(new URLRequest(''), 'quote')"/>
<mx:Button label="Back" click="PopUpManager.removePopUp(this);"/>
</mx:HBox>
Question: What code do I need to change above to be able to add more than one titleWindow (up to 6), or what code can I use for a hover to open a "window" or tool tip?
If anyone has any ideas or can direct me at all that would be great. I appreciate it!
Sounds like you just got thrown in the deep end. Generally speaking theres no limitation on the number of title windows you can have open, the PopUpManager class handles any UIComponent you tell it to open as a pop up and one of the arguments for the .createPopUp or addPopUp static methods on the manager will take a modal parameter which specifies if the user interaction should be blocked (indicated by blurring the application) or if the window should just be shown. It seems rather this error is stemming from your extension of the TitleWindow (the SimpleTitleWindowExample) can you post that code. Also as starting points on the topic check out the following documentation:
General Flex:
http://www.adobe.com/devnet/flex/videotraining.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/index.html
^ notice the runtimes and products selection options at the top of the screen, select according to SDK you're building with (or select SDK based on features)
PopUpManager:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/PopUpManager.html#createPopUp()
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/PopUpManager.html#addPopUp()
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/PopUpManager.html#removePopUp()
Tooltips:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/ToolTipManager.html#createToolTip()
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/mx/managers/ToolTipManager.html#destroyToolTip()
If you're still having troubles definitely post the code for the custom TitleWindow, also what version of the SDK you're using so I can try to replicate.
EDIT:
Ah okay seeing the rest of your code clears it up... you can't have two functions that have the same exact name so in your code you have showWindow as a function that is declared two times (I'm not sure how this is getting past compilation, I would have imagined the compiler would be smart enough to see this error before run-time, but it is what it is). Change the other showWindow to be something like showOtherWindow or something along those lines. It also looks like you come from a procedural programming background (C or some other non-OOP language) Object Oriented programming takes a little while to get your head around but makes a whole lot more sense when it comes to solving real world problems once you understand it, basically you're setting up descriptions and sending messages between objects using method calls and when you define a class, via AS or MXML you're defining the methods (order of method/property definitions doesn't functionally matter, things are still procedural, like step by step within the methods but the order of method declaration or property declaration has no effect).
This may be helpful:
http://www.codeproject.com/KB/architecture/OOP_Concepts_and_manymore.aspx
I'm basically at the opposite end of the spectrum in terms of skills, I have a formal education from DePaul University in computer science but at my current position am doing a lot of google maps flash code so I'm finding myself more and more needing to understand Datums and other GIS specialty info (just thought it was sort of interesting to find someone at the same intersection but going the other way :).

Where do I put custom code in a custom component in Flash Builder?

in the main file, I would write:
<components:mybutton id="mybutton1" rollOver="point_rollOverHandler(event)" />
But if I want the component to have that behaviour innately, where do I write it in the mybutton mxml file to have it reference itself?
I tried <s:rollOver="point_rollOverHandler(event)"/> but it complains the element type must be followed by either atrribute specifications, > or />
My guess is that your component is based on s:Button. Just declare the rollOver method in your components main MXML node like this (line 5):
// myButton.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Button xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
rollOver="button1_rollOverHandler(event)"
>
<fx:Script>
<![CDATA[
protected function button1_rollOverHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:Button>
in the root tag for your custom component add the property :
creationComplete="init()"
then in the script tag of your custom component create that function and set up the mouse event listener:
function init():void{
this.addEventListener(MouseEvent.MOUSE_OVER, point_rollOverHandler)
}
but this will call the function point_rollOverHandler(e:MouseEvent) defined in your custom component. If you're looking to call a function defined on it's parent then what you have is the best way. Otherwise you'd be tying the components too tightly together which would make your code brittle and less reusable.

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