Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1]
Print
Author Topic: Deferred Instantiation... Ughh!  (Read 8397 times)
coogle
Newbie
*
Posts: 4


View Profile Email
« on: August 23, 2008, 06:34:52 »

Hey all,

So I'll be the first to admit that I'm fairly new to Flex and AS3 in general, but so far I've been doing pretty well and I've settled on PureMVC as the architecture we'd like to use for this project.. However, I'm finding myself in Deferred Instantiation hell and it's really bothering me..

Here's the basic problem:

Main app has a view stack with a bunch of Canvas components.. I've read all the FAQs and saw the Slacker demo and I get it.. I create a few custom events which trigger the ApplicationMediator to create whatever mediators I want when each component is actually created by AIR. Fair enough..

My problem is past that point..

So consider if I have a ViewStack with two views.. Splash and Main.. I need to fire an event on creation of Main to register it's mediator, but now what of my Main component's components? Although the creationComplete event fired, and now my mediator is registered all of the sub-components still don't exist so in the constructor of my MainMediator, where I'd like to register event handlers (for instance, to handle an click event from a button in my component) viewComponent.myButton is *still* null.. which means I in my new component again have to create all of these dummy events to fire and then capture the dummy events in the mediator to then call real methods... This is absolutely annoying, because I basically have to create "fake" events for every real event that gets fired in my components because the component doesn't exist..

I've tried to do creationPolicy="all" on my canvas component (used in the viewStack) but that didn't do anything useful, and I really am hating the idea of having to pollute my application with a million THISBUTTON_CLICK:String = "someuniquestringforthisspecificbutton"; values so I can do click="dispatchEvent(new Event(THISBUTTON_CLICK))"... so I can then turn arround and finally do viewComponent.addEventListener(MainScreen.THISBUTTON_CLICK, onThisButtonClick)...

There must be a better way! Help Please!

John
Logged
danielcsgomes
Full Member
***
Posts: 47

 - daniel@onedesign.com.pt
View Profile Email
« Reply #1 on: August 24, 2008, 05:12:11 »

Hi John, i was the same problem and slacker demo got me the solution, i already made a demo that handles the deferred instantiation but in a diferent way and that register mediator to sub-components. take a look at this post and the demo it have view source:

http://forums.puremvc.org/index.php?topic=639.0

Daniel Gomes
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #2 on: August 24, 2008, 11:45:05 »

John,

I need to fire an event on creation of Main to register it's mediator, but now what of my Main component's components? Although the creationComplete event fired, and now my mediator is registered all of the sub-components still don't exist so in the constructor of my MainMediator, where I'd like to register event handlers (for instance, to handle an click event from a button in my component) viewComponent.myButton is *still* null..

What does your constructor look like?

If the constructor arg is not named viewComponent then you're referencing the viewComponent property of the Mediator itself, which is type Object.

If the constructor arg is called viewComponent, but is type Object, then viewComponent.myButton would refer to the parameter not the property.  It would not evaluate but would not cause error, since properties can be dynamically added to Object instances.

It would be best type the constructor argument anyway. But you'll also want to create a getter like:

:
private function get mainView():MainView
{
   return viewComponent as MainView;
}

And reference mainView.myButton.

Also, though not the answer to your question, it should be pointed out that although the demos don't all show this (but Slacker does):

You shouldn't set event listeners in your Mediator constructor, but instead, in onRegister. Also it's is necessary in MultiCore, but a best practice for both versions to hold off any facade interaction you'd be tempted to do in the constructor until onRegister as well.

The reason is you want your Mediator to be registered *before* any conversations can get started. If you register event listeners in your constructor, there's a chance the user could trigger the event *before* the Mediator is registered. The event listener on your Mediator would be called before your Mediator is registered, and possibly send a notification (yes, this is possible before registration), and be unable to respond to any Notifications sent back to it in response since it has not been registered yet.

Of course you could miss the event, as well, but in the case of a button or other user gesture, they can probably just click again. If the first click did nothing because it was missed, then that's less bad than if it started a process, which failed to complete and is an unpredictable state.

-=Cliff>
Logged
coogle
Newbie
*
Posts: 4


View Profile Email
« Reply #3 on: August 24, 2008, 12:59:11 »

Hey Cliff:

I understand the helper function to do my type casting, and I'm assuming the name of the variable in my constructor for the view object is not relevant, only what I pass to the parent's constructor via super()... The problem I am having is that, in that constructor, objects I would have expected to exist (viewComponent.myButton) are null references after the super(NAME, vc) call. I must have glossed over the notion of the onRegister() method in my readings so I'll take a second look at that and see if onRegister() solves my problem or not.. (the current problem being viewComponent.myButton in the Mediator's constructor is null).. To put it another way.. the component passed into my mediator's constructor exists, but it's children do not, which is very odd to me since the mediator's registration is triggered by the creationComplete event of the component, which I assume is called once the component creation is indeed complete..

Here is some more detail as well as a general AS3 / Flex question.. Consider this simple component code:

MyComponent.mxml
:
<mx:Canvas>
    <mx:Button id="myButton" creationComplete="dispatchEvent(new Event("Foo"))"/>
</mx:Canvas

Now.. in my "main app"

:
   <view:MyComponent creationComplete="facade.registerMediator();"/>

So what happens here is that the registerMediator() method will register the mediator for the MyComponent component but in the MyComponentMediator constructor (after the super() call) viewComponent.myButton is still null (this is my current problem)..

My question here though is this.. in the MXML document I was able to specify event handlers for what clearly are non-existent objects (as viewComponent.myButton is still null after the Canvas creationComplete event fires).. However, since they were statically defined at compile time the events must be bound to the object on-creation by Flex itself.. Surely this implies that Flex / AS3 has some sort of lookup table which stores the event handlers for non-existent objects until they are actually instantiated internally -- is there any way to access that mechanism at runtime to lazy-bind event handlers to not-yet-instantiated objects?


« Last Edit: August 24, 2008, 01:13:18 by coogle » Logged
coogle
Newbie
*
Posts: 4


View Profile Email
« Reply #4 on: August 24, 2008, 01:28:41 »

Just an update:

I tried using onRegister() to register my event handlers and they are still null :(

MyComponent.mxml
:
<mx:Canvas>
   <mx:Button id="myBtn"/>
</mx:Canvas>

Main.mxml (abbr.)
:
<view:MyComponent id="myC" creationComplete="dispatchEvent(new Event("mycreated"));"/>

MainMediator.as (abbr.)
:
...
public function MainMediator(vc:Main)
{
       super(NAME, vc);
       
       (viewComponent as Main).addEventListener("mycreated", onComponentCreated);

       if((viewComponent as Main).myC)
             facade.registerMediator(new MyComponentMediator((viewComponent as Main).myC));
}

protected function onComponentCreated(e:Event):void
{
     if(!facade.hasMediator(MyComponentMediator.NAME))
     {
         facade.registerMediator(new MyComponentMediator((viewComponent as Main).myC));
     }
}

MyComponentMediator.as
:
...
override public function onRegister():void
{
      // THIS IS THE CODE WHERE I GET A NULL REFERENCE ON myBtn
      (viewComponent as MyComponent).myBtn.addEventListener(MouseEvent.CLICK, onMouseClick);
}

Note that I've expanded out the whole (viewComponent as foo) stuff, I know I could have created a getter to do that for me.. The problem you can see is that last code block, onRegister() blows up on trying to add an event listener to myBtn, because myBtn == NULL and I have no idea why..

Hopefully that helps..

John
« Last Edit: August 24, 2008, 01:30:51 by coogle » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #5 on: August 24, 2008, 02:49:04 »

And you have verified this nullness by tracing or by placing a breakpoint and looking at it in the debugger? If you haven't done the latter, I'd suggest placing the breakpoint right after the call to super in the constructor.

-=Cliff>
Logged
coogle
Newbie
*
Posts: 4


View Profile Email
« Reply #6 on: August 24, 2008, 03:33:28 »

I just confirmed it... when my mainScreen canvas' mediator is created, during onRegister() this.mainScreen.adminBtn == null, as is all other children..

Am I mistaken to assume that when the component's creationComplete event is fired shouldn't that mean that all the children were created as well?

John
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #7 on: August 25, 2008, 09:58:37 »

Yes, it should. The code you've posted here isn't complete enough to tell much about what exactly the problem is. Try moving the breakpoint back to the component that's sending an event to the Mediator. If its still null, then you've ruled out your PureMVC apparatus.

-=Cliff>
Logged
alexbu
Newbie
*
Posts: 2


View Profile Email
« Reply #8 on: September 02, 2008, 01:24:23 »

I'm pretty sure, children are simply not exists yet before Flex calls creationComplete. So, onRegister call happens "now", and "creationComplete" call happens later, in the next frame. You create MyComponentMediator in the MainMediator constructor, which in turns calls onRegister. This is "now". "Later", "creationComplete" is called. But the mediator is there already.

I would say, the slacker demo with a deffered instantiation is nice until you need your new mediator immediately, for example in a command, where you first make a notification to AppMediator to switch a state and then wants to notify the new mediator....
« Last Edit: September 02, 2008, 01:30:54 by alexbu » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #9 on: September 02, 2008, 02:01:28 »

onRegister for a mediator only happens when you register said mediator. Why create your mediator before the view component exists? You can, of course do this and call setViewComponent later, but why would you do it? Defer creation of the mediator until the view component exists.

If you follow the normal PureMVC startup process, you don't actually create any mediators until after creationComplete because you use the application's creationComplete handler to call startup. But that doesn't mean that all your view components exist at that time. Read the Adobe Flex docs on deferred instantiation to get a better understanding of that concept.

-=Cliff>
Logged
alexbu
Newbie
*
Posts: 2


View Profile Email
« Reply #10 on: September 02, 2008, 02:55:33 »

probably, you didn't get me... My point is "do not create anything until it necessary" :) And this is happens:

1. I press the button in View, which throws Event and this Event is to be catch by Mediator. For example, the application's menu bar with a handler in ApplicationMediator.
2. This mediator uses sendNotification to force Command to do an action.
3. The command tells to the ApplicationMediator to switch to a state 'X' (by using sendNotification) and tries to tell to another Mediator (which is in a pair with a state's view) to do some job (by using sendNotification).

The problem on 3rd step, that the view's internal components are not initialized yet, so they are not available to the view's Mediator. Even more, View's Mediator will not exists because creationComplete on this View is not thrown yet (the view's mediator is created in this event). Looks like, Flex calls creationComplete in the next frame.

At the moment, I do following. I have a copy of such Commands with "initializing" stuff. Inside are only sendNotification to switch a state, registerCommand to reregister the current command to the real one and Application.application.callLater to throw the current notification again. With that, creationComplete handler creates my view's mediator and the real command can do the job than.
The other way would be check in a command, if the required mediator is exists and if not, just call itself later with callLater...
« Last Edit: September 02, 2008, 03:26:04 by alexbu » Logged
Pages: [1]
Print