tag:robotlegs.tenderapp.com,2009-10-18:/discussions/problems/660-rl-15-beginners-troubleRobotlegs: Discussion 2012-10-17T13:00:21Ztag:robotlegs.tenderapp.com,2009-10-18:Comment/196761082012-10-16T04:42:30Z2012-10-16T04:42:31ZRL 1.5 Beginner's trouble<div><p>Hi there.<br>
I recently moved from the basic mvc stuff I wrote up myself to RL,
heared alot of good things about it. When trying to mash up some
basic stuff, I encouter a problem saying that a service previously
mapped is null and I have trouble to wrap my head around
this...</p>
<p>So I punshed out a simple MainContext where I define the
following stuf:</p>
<p>public class MainContext extends Context implements IContext<br>
{</p>
<pre>
<code>public function TreeshirtContext(contextView:DisplayObjectContainer)
{
super(contextView);
}
override public function startup():void
{
commandMap.mapEvent(InitEvent.INITIALIZE, MyInitCommand, InitEvent);
injector.mapSingletonOf(IMyService, MyService);
super.startup();
dispatchEvent(new InitEvent(InitEvent.INITIALIZE));
}</code>
</pre>
<p>}</p>
<p>Then the Init Command, which is being called correctly:</p>
<p>public class MyInitCommand extends Command<br>
{</p>
<pre>
<code>[Inject]
public var event:MyEvent;
[Inject]
public var service:IMyService;
public function MyInitCommand()
{
service.init(); // here the error is being thrown
}</code>
</pre>
<p>}</p>
<p>and last but not least:</p>
<p>public class FacebookService extends Actor implements
IFacebookService<br>
{</p>
<pre>
<code>public function FacebookService()
{
trace("@constructor FacebookService");
super();
}
public function init():void
{
trace("@Service::init()");
}</code>
</pre>
<p>}</p>
<p>However, I get an error indicating that the service has never
been instantiated. The error is thrown in the Command, hinting that
the service has never been initialized.<br>
What am I missing?</p>
<p>Regards,<br>
Peter</p></div>Petertag:robotlegs.tenderapp.com,2009-10-18:Comment/196761082012-10-16T05:47:03Z2012-10-16T05:47:03ZRL 1.5 Beginner's trouble<div><p>The problem is you're calling service.init() in the command's
constructor,<br>
which is apparently being called before the service is constructed.
In this<br>
case Robotlegs is lazily constructing the service -- not actually
creating<br>
it until it's needed -- so the order is something like this:</p>
<ul>
<li>command is triggered</li>
<li>create MyInitCommand instance (call it's constructor) <--
your error here</li>
<li>Analyze the MyInitCommand for mappings to fulfill</li>
<li>Identify IMyService mapping, which is mapped but not created
yet</li>
<li>Create IMyService concrete implementation
(MyService/FacebookService instance)</li>
<li>Inject newly created instance</li>
<li>call command's execute() method <-- this is where you should
do work</li>
</ul>
<p>In a RL command, your "entry point" is a method named execute(),
so rather<br>
than calling service.init() in the command's constructor, you'll
want to<br>
create an overridden execute() method and call service.init()
there, e.g.:</p>
<p>override public function execute():void<br>
{</p>
<pre>
<code>service.init();</code>
</pre>
<p>}</p>
<p>An alternative pattern for initializing services is to use a
PostConstruct<br>
method in the service class. When you tag a method with the
[PostConstruct]<br>
metatag, that tells Robotlegs to treat the method like a
constructor for<br>
the class, only one where all the injections are available. (See
above for<br>
why injections aren't available in constructors.) So if you have a
method<br>
marked [PostConstruct] the order is something like this:</p>
<ul>
<li>some object that has an injection of your service is
constructed</li>
<li>Identify IMyService mapping, which is mapped but not created
yet</li>
<li>Create IMyService concrete implementation
(MyService/FacebookService instance), calling MyService()
constructor</li>
<li>fulfill any injections that the service requires ** Call
[PostConstruct] method in service</li>
<li>Inject newly created service instance where it's needed</li>
<li>continue doing stuff</li>
</ul>
<p>Note that while in this example I'm saying to use
[PostConstruct] in a<br>
service, you can really use it anywhere. Having said that, I'm
pretty sure<br>
I've only ever used it in a service.</p>
<p>Here's an example of what the service class would look like:</p>
<p>public class FacebookService extends Actor implements
IFacebookService<br>
{</p>
<pre>
<code>public function FacebookService()
{
trace("@constructor FacebookService");
super();
}
[PostConstruct]
public function init():void
{
trace("@Service::init()");
}</code>
</pre>
<p>}</p>
<p>The biggest potential downside to the [PostConstruct] approach
is that your<br>
service's init() method isn't called until the service is actually
needed<br>
for injection. So for example if your init() method kicks off
some<br>
asynchronous process that you'd rather have done at startup rather
than<br>
later, you'll definitely want to stick with the init command
approach<br>
you're currently using rather than using [PostConstruct]. I've used
both<br>
approaches depending on the needs of the app.</p>
<p>Paul</p></div>Paul Robertsontag:robotlegs.tenderapp.com,2009-10-18:Comment/196761082012-10-16T17:39:56Z2012-10-16T17:39:59ZRL 1.5 Beginner's trouble<div><p>Oh ofcourse,<br>
override public function execute():void ... is the function
supposed to call the service.init() methode. Silly me for not
seeing that.<br>
Thanks a bunch for the very detailed answer.</p>
<p>I indeed rather stick to the command approach, most of the stuff
I need to accomplish (right now) is basically bootstrapping stuff
and go on from there.</p>
<p>Thanks again, best regards,<br>
Peter</p></div>Peter