I have several custom components all of which are included in the parent application.
When I try to change state from a custom component, I get an error saying "undefined state: state name". How do I change the state of the application from within a custom component ?
To keep your custom component from being tied to your application I would recommend having the component dispatch a custom event that the parent application listens for. The parent application would then change its own state after receiving the event from the component. Hope that helps.
The simple but wrong way:
(parent as __application-class__).state = "__state-name__"
replacing the terms with underlines with their appropriate names. but as wade pointed out, it's a clumsy, brittle way to do it. If your application is small and not going to be seen by other developers though this is fine.
The right way of doing this would be setting up your main application to change its own state in response to some condition or input such as an event fired by another component.
Related
I'm starting to give up on the Flex forums so I'l re-post here and see how things go. Long time lurker, first time poster ;-)
I'm trapping keyboard events on the stage and dispatching custom events through a framework (Mate, though I don't know that this is an important issue), but I'm pretty confused by the scope. The structure is:
--application (traps keyboard events and dispatches custom event
-- mainPanel (receives dispatched events but mainCalendar (a child element)
is null on debugging. Obviously if I try to call a public method on
mainCalendar it errors out, even though it's a child of mainPanel.
However, mainCalendar is NOT null IF I use
FlexGlobals.topLevelApplication.mainPanel.mainCalendar.
Outerdocument and parentDocument do not expose mainCalendar either, BTW
-- mainCalendar (a custom component inside mainPanel with public methods.
Creation policy is 'all' and it's visible and exists long before I
trap any keystrokes at the top level)
Could someone take a minute and explain why the mainCalendar is out of scope when mainPanel receives a custom event, even though mainCalendar is a child of mainPanel. Is there a better way to manage the events so I don't have to always address the component via the topLevelApplication?
Thanks
(creating answer so this question could be marked as answered)
Usually, when reference to component is null and it should not, this means component isn't created yet. This might have place before application initialize event or if component's parent isn't visible and not validated.
ok so I have a TitleWindow that I open up... and I have 6 states defined. I am using the Presentation model pattern for all of my views. I have discovered a frustrating nuance. When I tell my window to go to XXX state, the controls have to initialize since the states in flex use lazy loading. so... my PM code that says myTextArea.text bombs out and say "cannot access..." so as a work around, I made some creationComplete events on my controls to register the control with the PM. So when the state changes, the textarea finally initializes and on creationComplete calls PM.registerTextArea() which sets a reference to it. then in that function I run my code... myTextArea.text.. etc.
This seems like such a ugly hack that I hate it. Is there any way to wait until the entire state in created then call code on the PM? I have tried enterstate... but this event seems to fire before the state controls are ready.
I tried to add a comment, but I guess editing is the only way I can do this...
to everyone: thanks for the great feedback. I am doing something slightly off straight PM. Every view has a viewController (as I call them). Its kinda my own hybrid of a delegate / dataprovider. But this is moot. It's the flex component lifecycle when dealing with states that is the pain. if you change state.. there is no event to say "all my components in this state are ready". only event to say "we changed state". so my code that fires on state change is trying to talk to components that aren't ready yet. So from what I see, there seems to be no design pattern or perfect way to ensure that all components are created in a state unless using creationComplete on every component in the state to register it is ready... if you don't, you get a race condition. Regardless of frameworks or design patterns, this seems to be a root issue.
The textarea is an easy PM fix.. just bind it to the pm value. But there are other times I can't.
Specifically, I am trying to attach video to a display once I get to that state. This is done via addchild. regardless of where I do it.. I need to know that the videoDisplay is done loading before I call addchild. I even tried currentStateChange event since docs say that fires last... but alas.. the components in the state are still initializing. So it seems that creationComplete is my only option. Maybe the only sane way to keep to clean code is to create the entire thing (videodisplay and video) using as once the state is entered. I just hoped the flex framework had events to ehlp me here rather than buiilding everything on the fly in as.
Since your PM has a reference to a visual component (myTextArea), this isn't a completely pure form of a presentation model. It appears to be more of a supervising presenter / controller type of setup.
That being said, the way I would fix your problem would be to use a complete presenter outright ( no knowlege of the view at all ) or use a complete controller ( view implements an interface through which the controller communicates ). The advantage of using a presenter in Flex is that you can create a bindable value such as text or selectedItem, and the view will bind to that variable whenever it comes online so the issues dealing with the lifecycle of Flex components go away.
In short:
I need an event listener in a custom component so all its instances (without editing them) react at the same time, fired by a dispatched event in its parent container.
In detail:
I have a custom component with Tab navigator. (The tabs are intended to show different preferences for different Languages.)
I have a button bar with buttons for all the languages.
There are a lot of instances of the custom component.
I want to click in a button of the languages bar and get ALL the instances switched to the same tab (the custom component contains the logic to change the tab).
I can do it by adding the event listener for EACH INSTANCE of the custom component, so it calls an internal function that changes the tab. But it seems to be very coupled, isn't it?
I wonder if it can be done in the master CLASS of the component, so it listen for events in its parent container, whichever it is.
In my mind this code shoud work, but it doesn´t (obviously ill'use a custom event to pass the new language value):
this.parent.addEventListener("lang_change", this.change_tab);
This way I can just drop an instance of the component, and see it working for itself.
Thank you in advance
I need an event listener in a custom
component so all its instances
(without editing them) react at the
same time, fired by a dispatched event
in its parent container.
The very thing you want to do, by definition, breaks encapsulation. In an ideal world, a component should know nothing of it's parent. If the component needs to communicate with it's parent, it should dispatch an event. IF a parent needs to communicate to children it should call a public method on that child (or change a public property). From an encapsulation stand point, I cannot recommend that the child listen for events on the parent.
I want to click in a button of the
languages bar and get ALL the
instances switched to the same tab
(the custom component contains the
logic to change the tab).
So, then put a click handler for the button and do something like this:
public function onClick():void{
myCustomTabNavigator1.selectedIndex = 1
myCustomTabNavigator2.selectedIndex = 1
myCustomTabNavigator2.selectedIndex = 1
}
You can also set the selectedItem if you a reference to it. , If you have your custom TabNavigators in an Array, you can loop over them. IF the custom TabNavigators are child of your custom component you can create a method in that custom component to set the defaults and call that method on each component instead of setting selectedIndex directly.
I think you should to use some MVC model like:
Cairngorm
http://code.google.com/p/swizframework/
http://www.robotlegs.org/
http://puremvc.org/
When I try to access the hidden TABs of my tab navigator control in action script, it returns a null error. But it works OK if I just activate the control in the user interface once. Obviously the control is not created until I use it. How do I make all the tabs automatically created by default ?
<mx:TabNavigator creationPolicy="all"/>
That should do it. Deferred instanciation is a feature, but sometimes it is a hassle.
The Flex framework is optimizing creation be default (creationPolicy="auto") so if you have a configuration dialog with a lot of tabs, for example, and the most useful tab is the first one, your application does not spend time and memory initializing the tabs that the user never sees.
This makes a lot of difference when dialogs like this never release, and is a good default to go with.
One thing to look at is using a private variable in your dialog/form instead of pushing the data to the control on the hidden page. This style treats the whole form as if it were a component, which it sort of is. To repeat: the MXML form/dialog/canvas is a class, and it can have data and methods in addition to containing other components.
Cheers
On a side note, I've run into the deferred-loading policy in a multi-state application, and circumvented it by forcing all elements to be included and invisible in the initial state. Something to consider, but only as a hack.
I have two mxml files. one is main that is application tag mxml file and another is my mxml component file.
I have a viewstack in my main mxml whose id is, say, "mainViewStack".
Now I want to set selectedChild property of "mainViewStack" from my mxml component file.
But I m getting error:
Error #1009: Cannot access a property or method of a null object reference.
on accessing mainObj.mainViewStack.selectedChild.id where mainObj is the object of main mxml file.
Please help me out.
Thank u.
My guess is that you're trying to access the child before it's created. But that's hard to tell without the code.
Try waiting until the FlexEvent.CREATION_COMPLETE event on the application to access the selected child.
This issue is referred to as "deferred instantiation" and is a product of the Flex Component Lifecycle. If you want an extremely thorough explanation of this concept, this white paper is probably the best I have read.
Essentially Flex creates components as they are needed. Each component has a lifecycle that takes it through stages:
Construction
Addition
Initialization
Invalidation
Validation
Update
Removal
A sub-component isn't going to be accessible until it has passed through the initialization phase. This is the point at which a Flex component will dispatch its CREATION_COMPLETE event, letting you (and the framework) know that it is ready for interaction. Prior to this event, you are going to receive null reference errors when attempting to access the component or its children.
ViewStacks compound this by default by not initializing sub-components until they are called for display. The creationPolicy property of a ViewStack, by default, is set to auto. There are several options for this property, including all. Be aware, however, that this can potentially present severe performance issues, as all of the components inside the stack are going to be initialized immediately regardless of whether or not the user actually even looks at the component.
In your specific case this isn't the problem. The component that contains the view stack hasn't been completely initialized. You need to set the child of the ViewStack in a CREATION_COMPLETE event handler.
or you can give "creationPolicy=all" because Flex only creates the first visible child from the viewstack
You can use this code in your MXML component to change application's ViewStack selected child from other MXML component. However it is not a good practice.
FlexGlobals.topLevelApplication.mainViewStack.selectedChild = FlexGlobals.topLevelApplication.childId
You can use static event dispatcher to dispatch an event from one view and listen that event in an other view.