IEventDispatcher null in Model
I know this is a pretty common problem (based on the threads I've read thus far), so I'm hoping there is a really simple answer. I've got a model that has the IEventDispatcher injected into it as such:
[Inject]
public var eventDispatcher:IEventDispatcher;
The problem is that when I try to access eventDispatcher, it is null. I have a config file that is mapping my model:
injector.map(GameModel).asSingleton();
...and the config is added to my context:
_context = new Context()
.install(MVCSBundle)
.install(RelaxedEventMapExtension)
.configure(ModelConfig, ViewConfig, CommandConfig, ServiceConfig, EventConfig, StartupConfig)
.configure(new ContextView(this));
StartupConfig also contains a reference to IEventDispatcher, and is successfully using it (i.e. it is being injected correctly). I'm sure there is something ridiculously simple I'm missing here..?
Comments are currently closed for this discussion. You can start a new one.
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by Shaun Smith on 03 Jul, 2013 11:37 PM
When are you trying to access it? You can't access it in the constructor because the property won't have been injected yet. See: https://github.com/robotlegs/robotlegs-framework/wiki/common-proble...
Hope that helps!
2 Posted by Kyle on 04 Jul, 2013 12:34 AM
Wish it was THAT simple ;-)
I'm trying to call it after some data is updated in the model via a setter method.
OtherClass -> GameModel.setData() -> GameModel.notifyUpdate() -> EventDispatcher.dispatchEvent -> (null object reference error on EventDispatcher).
Support Staff 3 Posted by Ondina D.F. on 04 Jul, 2013 07:25 AM
Hi Kyle,
I can think of 3 other possibilities in addition to what Shaun said:
you don't keep a reference to the context?
metadata stripping?
Model not injected?
You mapped it like this?
injector.map(WeirdModel).asSingleton();
And your Model looks like this?
And you injected it like this in SomeClass?
Or did you by any chance instantiate it like this?
weirdModel = new WeirdModel(); // bad
This wouldn’t work; The Injector won’t inject the IEventDispatcher into that Model.
So, if you don’t want to inject it, then let the injector instantiate the model:
weirdModel = injector.getInstance(WeirdModel); // good
4 Posted by Kyle on 04 Jul, 2013 08:23 AM
Hey Ondina!
I think number three (wierd model example) looks sort of like y
implementation... What's wrong with that example? Is it the
mapAsSingleton() piece or..?
Thanks,
Kyle
Support Staff 5 Posted by Ondina D.F. on 04 Jul, 2013 08:40 AM
Nah, the mapping is just fine.
Are you doing this:
weirdModel = new WeirdModel();
??
Can you paste the code where you instantiate the model?
6 Posted by Kyle on 04 Jul, 2013 02:49 PM
I have it mapped as a singleton, then injected into my service .
Shouldn't it be instantiated automatically when mapping as a singleton? (I
also pasted the line where I do the mapping in my original post for
reference)
Thanks and happy 4th!
-Kyle
Support Staff 7 Posted by Shaun Smith on 04 Jul, 2013 02:53 PM
Not exactly. The mapping won't instantiate it, it will only be instantiated on the first request (Injection point).
Support Staff 8 Posted by Ondina D.F. on 04 Jul, 2013 02:57 PM
If you injected it, it should work.
Maybe you call the service before the mappings are done?
Try to call the service in a context.afterInitializing handler and see what happens.
Support Staff 9 Posted by Shaun Smith on 04 Jul, 2013 03:00 PM
To debug this you could try creating a [PostConstruct] method on your model, then see what happens first (injection or your setter method). For example:
10 Posted by Kyle on 05 Jul, 2013 04:33 PM
Hey Sean/Ondina,
So I went ahead and ran with the [PostContstruct] attempt and as you both suspected, the model is trying to access the eventDispatcher before [PostContstruct] is called (the model is inject).
So the question becomes: Why is the model not injected yet, and how do I ensure that the model is created and has been injected before I run my StartupCommand (which begins loading the data that eventually calls the method on my model that tries to access the eventDispatcher).
Currently my Context / Setup routine looks like this:
The last config added "StartupConfig" is what kicks off my data loading cycle. Perhaps I should instead be listening for an event to notify me that the context is ready to rock, and then dispatch an event to fire off a command which houses my startup logic? Perhaps the context.afterInitializing that Ondina mentioned?
Thanks!
-Kyle
11 Posted by Kyle on 05 Jul, 2013 05:00 PM
My new setup looks like this:
After firing up, the startup method is called; however, eventDispatcher is null. Do I need to do something else to have the eventDispatcher inject into my root class, or is there a better way to dispatch an event out to the framework from here?
Thanks!
-Kyle
12 Posted by Kyle on 05 Jul, 2013 05:02 PM
It's also worth noting that my _context reference is null when the startup() method is called. I believe this is due to the method chaining on the context object. If I move the calls out into their own lines like this:
...the _context is available in the startup method; however, calling dispatchEvent via the _context object still does not reach the framework (trigger the command).
I also found a thread where it is mentioned that the "proper" way to handle a startup routine such as this is to write the startup logic in a config file (the original way I was handling this); however, I'm not sure how this should work since the config file is called before my actors have been injected (actors injecting, sounds so Hollywood! ha).
13 Posted by Kyle on 05 Jul, 2013 06:19 PM
Ok, this is officially becoming a bit maddening.. I figured out how to get an instance of the eventDispatcher off of the context and dispatch an event out to the framework; however, when the data model is reached, it still has not been injected.
So my flow is currently -> afterInitialization -> Disptach event to fire off service -> Service returns data and updates model -> Model tries to dispatch event to notify update but fails since it has not yet been injected with the eventDispatcher.
My new code is as follows:
I would assume that when the afterInitializing function is called that all injections should have taken place, but this is not the case... =\
Support Staff 14 Posted by Shaun Smith on 05 Jul, 2013 08:28 PM
OK, so the model is injected into the service? And you're definitely not "newing" it up yourself anywhere? Have you put a trace in the model's constructor to be sure that there's only one?
This tells me that your service is actually synchronous and your contextView object is already on stage. The context initialization is triggered by adding a view that is already on stage, and the whole context initialization process is completing (and calling the callback) before the context instance has been assigned to your field.
I have to run right now, but I'll try get back to this later.
15 Posted by Kyle on 05 Jul, 2013 10:02 PM
Hey Shaun,
I have verified that the Model's constructor is only being called once, and that there are no references to "new MyModel()" anywhere in the project. Does it seem like the way I am implementing this is correct, or is there perhaps some other way? Should the afterInitializing method be called AFTER all initialization/injection has completed (still trying to understand this piece a bit).
Thanks!
-Kyle
Support Staff 16 Posted by Shaun Smith on 06 Jul, 2013 11:21 PM
Heya,
You first context configuration example should work. I wouldn't pull the event dispatcher out using the afterInitializing callback like in your last example, but really, that should work too. It seems like there is something subtle going on here.
It occurs after all the configs have been run and the framework is fully initialized. That doesn't mean that all singleton objects have been created though - only those that have been requested.
It's really hard to figure out what is going on here without actual code to walk through. Could you create the simplest example app possible that demonstrates your issue? In the meantime though: what do your configs look like? (do they implement IConfig, or do they make use of
[PostConstruct]
?)17 Posted by Kyle on 07 Jul, 2013 05:24 AM
Hey Shaun,
Ill look into getting a simple example project together and see if I can
replicate the issue. My configs implant Iconfig and are not using the
postconstruct metadata.
From the way you're describing things it almost sounds like its a race
condition between the context creating and I jetting the model instance
when it is first requested and the command that is trying to set the
property on the model.
If that is the case it would seem like you would have to somehow first
ensure that the model had been injected before trying to set any properties
on it, which I would think would present a pretty major problem (see also a
common issue).
Any thoughts on that?
Thanks again for stickin with me on this one!
Kyle
Support Staff 18 Posted by Shaun Smith on 07 Jul, 2013 06:06 PM
No problemo! I'm sure we'll figure it out soon. I'd be very surprised if there was a bug in the framework that is causing this, but it's certainly possible.
Have you tried installing the
InjectorActivityLoggingExtension
to see what the injector is getting up to? It might provide some clues. Log everything!Support Staff 19 Posted by Ondina D.F. on 09 Jul, 2013 01:42 PM
I can confirm that the context is null in afterInitializing’s handler, when the contextView is already on stage by the time the context is created.
That happens in a pure as3 project, when the context is created in the root sprite. To avoid this, the context should be created in a root-view’s child.
In Flex it is possible to create a context on preinitialize or creation complete.
I saw that the StageSyncExtension is calling initialize() on the context.
If the contextView is not on stage yet, StageSyncExtension adds an event listener for ADDED_TO_STAGE and in its handler it calls _context.initialize();
If the contextView is already on stage, it calls _context.initialize(); right away.
In this case the context is null in all of the handlers:
.beforeInitializing(beforeInitializing), .whenInitializing(whenInitializing), .afterInitializing(afterInitializing)
The docs for the handlers say that beforeInitializing can be asynchronous, whenInitializing must be synchronous and afterInitializing must be synchronous.
The life of a Context is sooo complicated;)
To see what happens if the _context.initialize(); is delayed, I added a
Timer and after that it worked as expected, i.e. the handlers had access to the context.
I guess having a Timer is not a good solution though.
That’s also working, as Kyle has noticed:
20 Posted by Kyle on 09 Jul, 2013 02:33 PM
I initialize my context in the document class of my Flash Pro project, so
perhaps that is the culprit. Ondina, it sounds like the way to resolve this
is to create a child class that instantiated my context then create an
instance of that class and attach it to the displaylist in my document
class class. Does that sound about right?
Thanks!
Kyle
Support Staff 21 Posted by Ondina D.F. on 09 Jul, 2013 02:45 PM
Exactly!
@Shaun
ENTER_FRAME better than a Timer?
Support Staff 22 Posted by Ondina D.F. on 09 Jul, 2013 02:46 PM
It has to be a Sprite though.
Support Staff 23 Posted by Ondina D.F. on 09 Jul, 2013 03:26 PM
@Kyle I’ve attached an example for you.
ShellContextView is the ‘document class’
SomeView is the child mediated by SomeMediator, which dispatches SomeEvent on behalf of its view triggereing SomeCommand that accesses the injected SomeModel, which dispatches SomeEvent with the updated data, to which SomeMediator is listening and lets SomeView display the data. Ah, long sentence;)
24 Posted by Kyle on 09 Jul, 2013 04:01 PM
Thanks for the Example Ondina! I understand the reasons behind it, but having to create my Context in another child view just feels dirty for some reason haha. It seems like that is the solution for now, but my brain is screaming that the Context should be initialized inside the Document class. I'll give this a shot and see if it resolves the issues I was seeing, will report back shortly!
Support Staff 25 Posted by Ondina D.F. on 09 Jul, 2013 04:24 PM
You’re welcome!
Yes, I can empathize with what you're saying.
Let’s see if Shaun agrees with what I suggested to be done in StageSyncExtension, or if he comes up with a better solution to this.
My example works just fine. Try it out and add a service to it. If it doesn’t work, then something is wrong with your service class.
26 Posted by Kyle on 09 Jul, 2013 06:01 PM
I FOUND IT (it being MY error)! So after looking over the code about 1,000 times I finally noticed that I was initializing some Vectors in an init method within my model. No big deal, except for the fact that I was initializing them by creating a new Vector and assigning it to my property setter (as opposed to the private variable that the setter accesses). Calling the setter was causing my notifyUpdate() method to get called which in-turn tried to dispatch an event (with an IEventDispatcher that had not yet been injected). So looks like this one was on me (no terrible surprise there).
That being said, it seems we have raised a potential GOTCHA in the process (creating the Context in a child Sprite for AS3 only and Flash Pro projects). Thanks again for all the help on this one guys!
Support Staff 27 Posted by creynders on 11 Jul, 2013 08:09 AM
Hey @ondina,
what do you mean when you say
AFAIK the context isn't passed to the afterInitializing handler at all, only an instance of the LifeCycleEvent, so I don't quite get what 'context' you refer to?
Support Staff 28 Posted by Ondina D.F. on 11 Jul, 2013 09:43 AM
Hi creynders,
Welcome back :)
beforeInitializing, whenInitializing, afterInitializing, beforeSuspending, whenSuspending, afterSuspending, beforeResuming, whenResuming, afterResuming, beforeDestroying, whenDestroying, afterDestroying, they all return an IContext.
Context.as
When the contextView is already on stage, which happens when you create the context in the document class of a as3 project or in Flex onAddedToStage, the _context.initialize(); runs immediately inside of StageSyncExtension.handleContextView(). I don’t quite understand what’s happening there, but the lifecycle handlers seem to have some issues ( sync / async ???).
StageSyncExtension
If the context initialization takes place in a Flex preinitialize or creationComplete handler, or in a sub-view of a document class in as3, StageSyncExtension waits for the view to be added to the stage before initializing the context, and the lifecycle handlers return an IContext as expected.
Not sure if this is the solution, but after delaying the context initialization inside of StageSyncExtension.handleContextView by using a timer or this:
the lifecycle handlers return an IContext even if the contextView is already on stage.
Maybe the culprit is somewhere else, but I can’t find it, and I don’t know what tests should I run to catch it.
Context creation and configuration and the handlers that should return an IContext:
Support Staff 29 Posted by creynders on 11 Jul, 2013 11:36 AM
The reason why all the traces in the handlers return null is because they're called before the context instance is stored in the _context property. This is one of the subtleties we need to be aware of when calling method chains that execute synchronously.
I.e., it doesn't have anything to do with the context view already being on the stage. This should solve all problems (no need to initialize the context in a sub-view etc.)
just store the context instance in a property, BEFORE starting the method chaining:
As a rule of thumb, I never start chaining immediately after instantiation exactly to avoid this pitfall. Probably we should point this out somewhere in the documentation.
Support Staff 30 Posted by Ondina D.F. on 11 Jul, 2013 12:13 PM
Yes, that’s already been discussed in the previous posts. [http://knowledge.robotlegs.org/discussions/robotlegs-2/3724-ieventd...]
Ok, then we don’t need the StageSyncExtension at all.
We could do just this:
Oh yes, we should!!!