Flex - Passing data into tabbed views - apache-flex

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

Related

flex Property ignored

I'm trying to get an itemrenderer to display the text from a tweet, and (if there is one) the uploaded image. Text displays fine, but no matter what I do, the image doesn't show. Could it be that because I'm refering to a property that does not always exist, in this case "data.entities.media.creative.media_url" (it only exists if there is an image uploaded with the tweet) that the itemrenderer just ignores the property for all tweets?
Here's my code:
[Bindable(event="dataChange")]
public function get data():Object
{
return _data;
}
public function set data(value:Object):void
{
_data = value;
dispatchEvent(new Event("dataChange"));
}
]]>
</fx:Script>
<s:Label text="{data.text}"
width="100%"
styleName="tweetlist"/>
<s:Image id="tweetImage" source="{data.entities.media.creative.media_url}"/>
I am quite new to Flex, and so far I managed to get things working by pasting code together from different sources. So, any help would be greatly appreciated.
I see that you are binding to a deeply nested property: data.entities.media.creative.media_url. If you want the binding to work properly, you must make sure that each and every property on that path is made Bindable.
Concretely:
data must be Bindable on the ItemRenderer; it is by default when you extend ItemRenderer, so I would remove your custom Bindable declaration and event dispatching (or even remove the data getter/setter override altogether)
the entities property of the data instance must be declared Bindable
the media property of the entities instance must be declared Bindable
and so forth all the way to the media_url property
However it is not considered good coding practice (in general, not just in Flex) to access deeply nested properties like that, so I would advise you to encapsulate the property that you need so that you can bind to it like this:
<s:Image id="tweetImage" source="{data.media_url}" />
This will not only be better from a "clean code" point of view, but also for performance if you have a lot of items in the List. (Because the number of bindings will descrease by factor 5)
An alternative solution is to not use data binding and simply set tweetImage's source property:
override public function set data(value:Object):void {
super.data = value;
tweetImage.source = data.entities.media.creative.media_url;
}

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.

How can we create linkbuttons whose text is data-driven, but whose visibility is controlled from the front-end?

I've made a component based on a mx:TitleWindow that contains linkbuttons that I'm using as a context-menu.
The TitleWindow component contains link buttons like this:
[Bindable]
private var _showEmailThis:Boolean = false;
[Bindable]
private var _showApproveThis:Boolean = false;
[Bindable]
private var _showReviewThis:Boolean = false;
<mx:LinkButton id="lnkEmailThis"
visible="{_showEmailThis}"
includeInLayout="{_showEmailThis}"
click="lnkEmailThis_click()"
label="Email this!" />
<mx:LinkButton id="lnkApproveThis"
visible="{_showApproveThis}"
includeInLayout="{_showApproveThis}"
click="lnkApproveThis_click()"
label="Approve this!" />
<mx:LinkButton id="lnkReviewThis"
visible="{_showReviewThis}"
includeInLayout="{_showReviewThis}"
click="lnkReviewThis_click()"
label="Review this!" />
This component contains a large number of links and is re-used by multiple modules in the same application.
Within each module, this component is used when a user clicks on a row in a datagrid.
The code looks like this:
In "Requests" module:
private function dgRequests_click(event:MouseEvent):void
{
menu.showApproveThis = true;
menu.showReviewThis = true;
}
In "Performance" module:
private function dgPerformance_click(event:MouseEvent):void
{
menu.showEmailThis = true;
menu.showReviewThis = true;
}
As you can see, the visibility of individual linkbuttons is controlled within each module by setting boolean properties.
If I alter this component to use a renderer, how can I control the visibility of the linkbuttons from the code in each module (there are over a hundred links with different functionality - not every link will be used in each module)? Note: it is not known from the back-end which grid within which module uses which link. This is set in the front end within each module's actionscript file.
e.g., if the XML looks like this:
<links>
<link>
<label>Email This</label>
<visible>_showEmailThis</visible>
</link>
<link>
<label>Approve This</label>
<visible>_showApproveThis</visible>
</link>
<link>
<label>Review This</label>
<visible>_showReviewThis</visible>
</link>
</links>
and I set the Email link to show in the module like this:
private function dgPerformance_click(event:MouseEvent):void
{
menu.showEmailThis = true;
}
How do I make it take effect? Is there a way to control the visibility of a linkbutton within an item renderer in a component from the module that uses that component?
I'm not really sure I understand your question right. But if you have a component (or a group of 'em) sequentially repeating several times with different properties' values you should use mx:Repeater.
Adobe's docs on Repeater
There is also a live example in TourDeFlex (which I strongly recommend to install and use).
Looks like this is not possible.

flex databinding with selectedItem property of the combobox update many times problem

well, I have a combobox which I have bind his selectedItem property to a value object object, like this
<fx:Binding source="styles_cb.selectedItem.toString()" destination="_uniform.style"/>
<fx:Declarations>
<fx:XML id="config_xml" xmlns="" source="config.xml" />
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:ComboBox x="66.15" y="63.85" editable="false" id="styles_cb" dataProvider="{config_xml.styles.style}" />
the value object is a custom class with some setters and getters, and I want to set a property based of the value of the selectedItem of the combo, so inside the value object I have something like this
[Bindable]
public function set style(value:String):void
{
_style = value;
trace(value);
}
my problem is that each time I change the combobox selection which in fact change the style property of the value object it does it 3 times, if I trace the value of the setter it actually do the trace 3 times, why?? how can I avoid this? I'm doing something wrong? or there is a better way to do it, please help, thanks for any help
It's common for data binding expressions to fire many times more than one would expect. I don't know the exact reason. If this causes issue for your app, then don't bind the source directly to the target, instead use invalidation. Bind the source to set a flag, stylesSelectedItemChanged and call invalidateProperties(). Then override commitProperties() and inside your commitProperties(), check if stylesSelectedItemChanged is true, and if so, propagate the new value forward to the destination and reset the flag to false. Be sure to also call super.commitProperties() or else many things would break.
Invalidation is extremely common in the Flex framework, every component uses it internally, and it helps a lot with these kinds of issues.
wow!!, some times writing a question let you think about it twice and let you find the answer by yourself, so I find my own solution, in the documentation said I can make all the properties of an object bindables if I put [Bindable] in the class declaration, so I did it like this
[Bindable]
[RemoteClass(alias='Uniform')]
public class Uniform extends Object implements IEventDispatcher
however when I was trying to dispatch an event in the setters I found in the docs that I must add the event name like this
[Bindable("styleChanged")]
public function get style():String
{
return _style;
}
public function set style(value:String):void
{
_style = value;
dispatchEvent(new Event("styleChanged"));
}
now I found that doing this, mark the property with a double bind and that was making me set the property many times, hugg!, but now I know I can avoid using the second [Bindable] and still the event get dispatch, so now I wonder why I need to use [Bindable("styleChanged")] in the first place if I still can dispacth the event with only [Bindable] and the dispatch method?, weird
hope this help to someone

Flex - Sending a parameter to a custom ItemRenderer?

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

Resources