Second model Injection not working ie hitting null value when executed

ian's Avatar

ian

31 Mar, 2011 11:50 AM

Hello everyone, I am new to RL but I have a problem which I can't resolve. My application called 'Jane' loads XML and builds a textual (and eventually filmic) scenario from it.

The problem is with the ScriptModel of the XML once loaded and parsed.

In the context it is mapped thus:

injector.mapSingleton(ScriptModel);

// with its relevant application event commands
  commandMap.mapEvent( ApplicationEvent.SCRIPT_PARSED, WriteScenarioCommand, ApplicationEvent, false);

This has been successfully injected into two classes. Firstly, the ParseScriptService

public class ParseScriptService extends Actor implements IParseXML
{

    private var _myLoader:URLLoader;

    [Inject]
    public var scriptModel:ScriptModel;

}

This class loads the XML and hands off its content successfully to the ScriptModel via a public method it has
which then dispatches the ApplicationEvent.SCRIPT_PARSED event that lauches the WriteScenarioCommand, which should use selection algorithms on its content.

// In ParseScriptService

public function onContentLoad(event:Event):void{

        trace("fully loaded");
        scriptModel.setScriptXML(event.target.data);

}

//In ScriptModel

public function setScriptXML(dataObj:Object):void{

        _scriptXML = new XML(dataObj);
        _scriptXML.ignoreWhitespace = true;

        _conversation = new XMLList(_scriptXML.conversation);
        _dictionaryModel.setDict(_scriptXML.dict);
        _projectInfoModel.setInfo(_scriptXML.info);

        dispatch(new ApplicationEvent(ApplicationEvent.SCRIPT_PARSED));

}

So far so good.

This command then instantiates a SectionWriter instance that extends Actor (so it is a RL class) that has the ScriptModel also injected in it:

public class SectionWriter extends Actor implements IWriter

{
    private var _selectMethod:ISelector;

    [Inject]
    public var script:ScriptModel;


    [Inject]
    public var scenario:ScenarioModel;

    public function SectionWriter()
    {
        super();
        trace("SectionWriter constr");
    }

    public  function write(sect:SectionVO):void
    {   
    //fails here
    var sectionXML:XMLList = script.conversation.(@id==sect.conversation).section.(@position==sect.section);
            //etc....
            }

}

But as soon as the write() method is invoked on it, which relies on the ScriptModel the debugger says it does not exist i.e. it is null. Thus injection does not appear to work on the second time of asking.

I tried the same ScriptModel injection in in the Command class, just to check, and there it works as expected:

package com.edenserpent.jane.command
{

import com.edenserpent.jane.model.ScriptModel;
import com.edenserpent.jane.model.selection.SectionWriter;
import com.edenserpent.jane.model.vo.SectionVO;

import org.robotlegs.mvcs.Command;

public class WriteScenarioCommand extends Command
{
    private var _sectionWriter:SectionWriter;

    [Inject]
    public var script:ScriptModel;

    public function WriteScenarioCommand()
    {
        super();    
    }

    override public function execute():void
    {
        //works fine. Injection here successful
        var sectionXML:XMLList = script.conversation.(@id==1).section.(@position==2);
        trace(sectionXML);


        var sectionVO:SectionVO = new SectionVO(6,1);

        _sectionWriter = new SectionWriter();


        // falls down here as this method relies on ScriptModel being successfully injected
        _sectionWriter.write(sectionVO);

    }
}

}

What's going on? Any ideas, anyone?

  1. 1 Posted by Stray on 31 Mar, 2011 12:25 PM

    Stray's Avatar

    Hi Ian, the problem is this:
    >
    > _sectionWriter = new SectionWriter();

    When you create a class that needs injection, you have to use the Injector to do it.

    Injection isn't 'magic' - it just feels a little that way some times.

    There are some options about how you do that:

    1) You could map the SectionWriter as a Singleton as inject it as a variable into the relevant command. (It seems like the SectionWriter probably isn't a Singleton).

    2) You can use the injector in the command to get an instance that has been injected into:

    _sectionWriter = injector.instantiate(SectionWriter)

    would do it.

    or... forget auto DI in this case:

    3) As you already have the model injected in the command, you could just pass it to the write() function (this is actually still DI, just not automated DI)

    _sectionWriter.write(script, sectionVO);

    That should sort you out - the choice of method is yours...

    Stray

    ps: Interfaces dude. Interfaces. Avoid injecting concrete classes FTW!

  2. 2 Posted by ian on 31 Mar, 2011 07:58 PM

    ian's Avatar

    Hi Stray,

    Thanks a lot for that reply. It certainly helped. I have been trying to implement your suggestions but with mixed results.

    Firstly, I did manage to pass in the Models I formerly tried to inject as parameters into the SectionWriter but when it got to a dispatch() call to communicate with the rest of the framework, the eventDispatcher in the Actor superclass it extended had a null reference in the debugger, despite it being fine elsewhere.

    Then, hey presto (actually after a lot of frustrating work - injections must be public properties!) I successfully mapped the SectionWriter as a Singleton.

    In the Context it was thus:

    //NB the interface!

    injector.mapSingletonOf(IWriter, SectionWriter);

    Then in the command class, WriteScenarioCommand, I used:

    public class WriteScenarioCommand extends Command

    {
        private var _sectionWriter:IWriter;
    
        public function WriteScenarioCommand()
        {
            super();    
        }
    
        override public function execute():void
        {
            var sectionVO:SectionVO = new SectionVO(6,1);
            _sectionWriter = injector.instantiate(SectionWriter);
            _sectionWriter.write(sectionVO);
    
        }
    }
    

    However, notice I did not inject it as such into the Command class as you suggested. Is that a no-no? This seems like a little bit of your first and second suggestions rolled into one.

    Anyway, this set off the SectionWriter, and got it performing the line selection fine and in this case, the dispatch() call worked.

    Thanks again for your input, Stray.

  3. 3 Posted by Stray on 31 Mar, 2011 08:17 PM

    Stray's Avatar

    Hi Ian - there is an excellent 'common problems' document - I think it's top of the 'Recent Articles' list:

    https://github.com/robotlegs/robotlegs-framework/wiki/common-problems

    That will help you avoid most of the niggles such as trying to inject private properties or accidentally putting a ; after the [Inject] tag.

    I'd recommend that you use your Singleton injected by interface (no point making them if you don't use them) - so your code would become:

    // in the context

    injector.mapSingletonOf(IWriter, SectionWriter);

    // in the command - make sure you inject against the interface

    [Inject]
    public var _sectionWriter:IWriter

    override public function execute():void
    {
    var sectionVO:SectionVO = new SectionVO(6,1);
    _sectionWriter.write(sectionVO);
    }

    That's 'the robotlegs way' - it'll also get you on the right path to understand the examples and move forward with more code.

    Any class that extends Actor HAS to be instantiated by the injector - otherwise its eventDispatcher won't be provided for it.

    In fact - golden rule - if your class extends any of the framework classes: Actor, Command, Mediator - then you should not do new MyClass();

    The only one you can 'new' is your context.

    The reason for that is that without the injector these classes won't have all their dependencies injected and they'll be just sitting there waiting to puke a null pointer error.

    So - onwards! Good luck with the next steps, I promise it really does all make sense once you find your rhythm.

    Stray

  4. 4 Posted by ian on 31 Mar, 2011 08:51 PM

    ian's Avatar

    Hi Stray,

    That is all very helpful. Great to have such informative and clear support from the RL community.
    I changed the Command class as you said, and it's working fine.

    The tips regarding not instantiating any of the framework actors with new() except for the Context is also pretty key.

    Cheers!

  5. Support Staff 5 Posted by Stray on 02 Apr, 2011 11:34 AM

    Stray's Avatar

    No worries! I'll close this now - feel free to open a new thread if/when you have more questions.

  6. Stray closed this discussion on 02 Apr, 2011 11:34 AM.

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