I am having an issue with flex states in my application. What I am looking to do is on creation complete of the application, obtain a user role guest/user/superUser (based on username and password) from a server, then set the state client side based on that information. My .mxml classes need to include certain graphic elements based on that state. I am running into the issues of including elements based on the states defined at the Application level of the project. I am trying to avoid having to define the states in each .mxml file that needs it.
I feel that this is possibly something easy that I am overlooking or maybe there is a better way to do this. Any examples input is very helpful.
I know that this returns the current state
Application.application.currentState;
but I am looking to almost "populate" the
<mx:states>
<mx:State name="state1"/>
</mx:states>
of every .mxml file from the states defined in the Application
If you are looking for dynamic states - here is your solution (The first two states – default and big - are added at compile time. The third state Bang-a-Gong is added at runtime):
private function init():void {
// Create a new state and give it a name.
var stateBang:State = new State();
stateBang.name = 'Bang-a-Gong';
// Set the overrides with an array of AddChild, AddItems,
// RemoveChild, SetEventHandler, SetProperty, and SetStyle
stateBang.overrides =
[ new SetProperty( btn, "label", "Bang-a-Gong" ),
new SetProperty( btn, "height", "150" ),
new SetProperty( btn, "width", "300" ),
new SetStyle( btn, "fontSize", "22" ),
new SetStyle( btn, "fontWeight", "bold" ),
new SetStyle( btn, "color", "#FF0000" ) ];
// Add our new state to the available states of this component.
this.states.push( stateBang );
// Just for kicks lets add a transition for this state.
var transition:Transition = new Transition();
transition.toState = 'Bang-a-Gong';
// Create a new transition effect.
var resize:Resize = new Resize( btn );
// Create an composite effect, either: Sequence or Parallel.
var sequence:Sequence = new Sequence();
// Add our resize effect.
sequence.addChild( resize );
// now add our composition effect to the transition we created.
transition.effect = sequence;
// Push our new transition into the transitions array for this component.
this.transitions.push( transition );
}
In another case, if I understood in right way, you should create some Object in main application, and access it from all child components via FlexGlobals.topLevelApplication.
If you want to change child states, you should have some instance with already defined states as minimum in one place, and later copy them, to child components, but what sense if they are all custom logic?
So, let me know, if it helps.
Related
My custom Widget have a collection of child widgets that are constructed on AJAX response. So I need to maintain an array of child widgets and append the domNode of those widgets to its parent Widget's domNode.
I can use a dijit.WidgetSet make a collection of the child widgets but there is no dijit.WidgetSet.domNodes() method to get domNodes of all the widgets. also dijit.WidgetSet doesn't take care of placement of child nodes in its parent DomNode
Is there any other class for doing the same already ? e.g. would subclassing dijit.WidgetSet to take care of appending to parent domNode be a reinvetion of Wheel ?
If you extend your parent with dijit._Container you can get hold of your widgetset by calling parent.getChildren(). The order in which widgets are returns is afaik the order that they were added to parent (parent.addChild()) and not by DOM sibling-hood. However, once you use the parser, this ofc would be the same. Well, any widget would have a getChildren implemented but the difference for _Container class is, that the addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex) and removeChild: function(/*Widget|int*/ widget) functions are pretty nifty. If you put an insertIndex while you add children, sibling-hood is manageable.
For concern of domNodes, with dijit we go 'beyond' DOM and work in the widget layer instead - which is only a JS wrap. The chainable function '.domNodes()' does not exist anywhere in dojo, you instead need to call similar to following:
parent.getChildren().forEach(function(childWidget) {
var domNode = childWidget.domNode;
...
});
// this would get nowhere as the return child-set is merely a 'stupid' array
parent.getChildren().set("attribute", "value");
Here is a cute little wrapper for the above foreach, using dijit/map
// retreives an array of the actual domNodes of widgetset
dijit.registry.map(function(widget){return widget.domNode;}).forEach(
// uses hitch to allow passing parameters without wrapping in new function
dojo.hitch(
window, // on random scope
dojo.addClass, // calling function
"imTheChildDomNode"// arguments[0]
)
);
I have a menu, each time you click on an item it opens up a screen (a new flex component), when I click back on to the screen I want to dispose of that particular instance, is there an easy way to do this?
UIComponent.removeChild(child) is one way. You can also do this with View States.
The best way to do it is to store all your dynamic instances in an Array, like dynamicHandles:
var dynamicHandles:Array = new Array();
dynamicHandles["test"] = new MCTest();
And then add as children:
addChild(dynamicHandles["test"]);
Finally, whenever you need to remove them, first remove them as child and then clean up the array like so:
removeChild(dynamicHandles["test"]);
dynamicHandles = new Array();
I'm using a tree control that I want to customize. The data items in the tree's dataProvider have a property name that should be used for labeling the node, and a property type that should be used to select one of several embedded images for use as an icon. The simplest way to do this is by using the labelField and iconFunction properties.
However, I wanted to get started with item renderers and open the door for adding more complex customization later, so I tried making my own item renderer. I extended the TreeItemRenderer class as follows and used it in my tree control:
class DirectoryItemRenderer extends TreeItemRenderer
{
[Embed("assets/directory/DefaultIcon.png")]
private static var _DEFAULT_ICON:Class;
// ... some more icons ...
override public function set data(value:Object):void
{
super.data = value; // let the base class take care of everything I didn't think of
if (value is Node) { // only handle the data if it's our own node class
switch ((value as Node).type) {
// ... some case clauses ...
default:
this._vSetIcon(_DEFAULT_ICON);
}
this.label.text = (value as Node).name;
}
}
private function _vSetIcon(icon:Class):void
{
if (null != this.icon && this.contains(this.icon)) {
this.removeChild(this.icon);
}
this.icon = new icon();
this.addChild(this.icon);
this.invalidateDisplayList();
}
}
This code has no effect whatsoever, icon and label in the tree control remain at their defaults. Using trace(), I verified that my code is actually executed. What did I do wrong?
Looking at the base mx.controls.treeClasses.TreeItemRenderer class, I see that in the updateDisplayList function the renderer gets it's icon and disclosureIcon classes from _listData:TeeListData. Instead of overriding the updateDisplayList function, try modifying the icon and disclosureIcon classes of the renderer's private _listData instance in your _vSetIcon method using the public accessors, like so:
private function _vSetIcon(icon:Class, disclosureIcon:Class = null):void
{
var tmpListData:TreeListData;
if (disclosureIcon == null) disclosureIcon = icon;
tmpListData = this.listData;
tmpListData.icon = icon;
tmpListData.disclosureIcon = disclosureIcon;
this.listData = tmpListData;
}
EDIT
Here is some clarification on the difference between data and listData. You'll have to excuse my omission of package names but I'm editing from my phone so its tough to look them up and I don't know the package names off the top of my head. data is defined in the context of a TreeItemRenderer in the IDataRenderer interface. You create a data renderer by implementing this interface and defining a public property data, which in this case is set by the parent control and contains some data and meta-data from the dataProvider to be rendered by the data renderer class.
listData is defined in the IDropInListItemRenderer interface as a property of type BaseListData and is realized in the TreeItemRenderer class as a property TreeListData. It differs from the data property in that it contains meta-data that describes the TreeListRenderer itself (icon, indent, open) as well as (I believe, I'll have to double check this later) a reference to the data item being rendered. I gather that It's used by the the TreeItemRenderer and I would imagine the parent list control for display update and sizing purposes. Someone is free to correct or add onto that if I'm incorrect or missed something, I'm going of what I remember drom the code.
In this case, you wanted to use meta-data from the data set from the data provider to modify data that determines the display of the renderer, so you would need to modify both.
I think the real confusion here however came from the fact that you extended the TreeItemRenderer class then tried to override functionality on the component in a manner the original developer didn't intend for someone to do, hence the unexpected results. If your goal is education and not ease of implementation you would probably be better served by extending the UIComponent class and using the TreeItemRenderer code as a reference to create a class that implements the same interfaces. That would be a real dive into the pool of custom component development.
I'd probably try something simple, as in this example from the Adobe Cookbooks. I notice that they override updateDisplayList, which may have something to do with your problems.
There's another example (for Flex 2, but looks applicable to Flex 3) that shows how to manage the default icons. It looks like you'll want to manage the icon yourself, setting the default icon styles to null, instead of trying to manipulate the superclass's icon property.
Update -- Looking at the source for TreeItemRenderer, commitProperties has the following before checking the data and setting up the icon and label:
if (icon)
{
removeChild(DisplayObject(icon));
icon = null;
}
Also, it looks like the setter for data calls invalidateProperties. Hence, your icon is wiped out when the framework gets around to calling commitProperties.
I've reviewed all the documentation and Google results surrounding this and I think I have everything setup correctly. My problem is that the symbol is not appearing in my app. I have a MovieClip symbol that I've embedded to my Flex Component. I need to create a new Image control for each item from my dataProvider and assign this embedded symbol as the Image's source. I thought it was simple but apparently not. Here's a stub of the code:
[Embed(source="../assets/assetLib.swf", symbol="StarMC")]
private var StarClass:Class;
protected function rebuildChildren():void {
iterator.seek( CursorBookmark.FIRST );
while ( !iterator.afterLast ) {
child = new Image();
var asset:MovieClipAsset = new StarClass() as MovieClipAsset;
(child as Image).source = asset;
}
}
I know the child is being created because I can draw a shape and and that appears. Am I doing something wrong? Thank you!
You should be able to simply set child.source to StarClass:
child = new Image();
child.source = StarClass;
See the MovieClipAsset Language Reference for more details:
you rarely need to create MovieClipAsset instances yourself
because image-related properties and
styles can be set to an
image-producing class, and components
will create instances as necessary.
For example, to set the application
background to this animation, you can
simply write the following:
<mx:Application backgroundImage="{backgroundAnimationClass}"/>
I have set the itemRollOver and itemRollOut event listeners on a List component, but whenever I roll the mouse over a list item, both the over and out events of the same list item fire in succession right after each other. My list uses a custom itemRenderer.
Any ideas why this might be? The Adobe documentation doesn't provide much insight into this (not surprisingly...).
In my opinion this is a bug. The ListBase.mouseOverHandler now sets a variable called lastHighlightItemRendererAtIndices when it dispatches an ITEM_ROLL_OVER event, which is then used (together with lastHighlightItemIndices) when dispatching an ITEM_ROLL_OUT event in ListBase.clearHighlight (called by the mouseOutHandler).
The problem is that when you mouse from row-to-row the mouseOverHandler is called first, setting the lastHightlight... variables, and then when the mouseOutHandler gets called subsequently, it uses the lastHighlight... values that were just set with the result that you get consecutive 'roll over' and 'roll out' events for the same renderer.
Frankly I don't know why ListBase.clearHighlight just doesn't use the passed in renderer when dispatching the ITEM_ROLL_OUT event (which is how it used to work in SDK 2) as this is the actual renderer that is being 'rolled out of'.
Are they coming from the same object?
If not you will it is likely so that you will get an itemRollOut from the "item" you just left and a itemRollOver from the new one you entered, depending on their spacing and such these may fire very close to each other.
Make sure you are setting super.data in your item renderer if you are overriding set data().
ListBase listens for MOUSE_OVER and then figures out the item underneath it based on coordinates of mouse and the position of the item renderer. You could check ListEvent.itemRenderer to see which renderer's roll over and roll out are firing and in what order.
Worst case, you could listen for rollOver and rollOut inside your item renderer.
Had the same problem. super.data was already being set, and the item is the same for the rollOut and rollOver event. I ended up opting for anirudhsasikumar's worst case scenario, and listened for rollOver and rollOut inside the item renderer. Seems to work fine.
I was having this same issue. I ended up subclassing the mx.controls.List class and overriding the clearHighlight function. As far as I can tell, the lastHighlightItemIndices variable is only ever read in that function. So doing something like the following fixed this issue:
import mx.core.mx_internal;
use namespace mx_internal;
public class List extends mx.controls.List
{
public function List()
{
super();
}
override mx_internal function clearHighlight( item:IListItemRenderer ):void
{
var uid:String = itemToUID( item.data );
drawItem( UIDToItemRenderer( uid ), isItemSelected( item.data ), false, uid == caretUID );
var pt:Point = itemRendererToIndices( item );
if( pt )
{
var listEvent:ListEvent = new ListEvent( ListEvent.ITEM_ROLL_OUT );
listEvent.columnIndex = item.x;
listEvent.rowIndex = item.y;
listEvent.itemRenderer = item;
dispatchEvent( listEvent );
}
}
}
Then just use this List class instead of the Adobe one and you'll have the behavior you expect. I tested this against Flex SDK 3.2 and it works.
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:controls="com.example.controls.*">
[ other code ... ]
<controls:List itemRollOver="onItemRollOver( event )" itemRollOut="onItemRollOut( event )" />
</mx:Canvas>
Thanks to Gino Basso for the idea in the post above. Hope that helps.
Thanks for the solution. That really solved the problem! Small correction, though:
listEvent.columnIndex = item.x;
listEvent.rowIndex = item.y;
should be
listEvent.columnIndex = pt.x;
listEvent.rowIndex = pt.y;
item.x and y hold the coordinate of the renderer in pixels.