injected model = null - without typo, it is mapped, not in constructor!

iamable's Avatar

iamable

Nov 13, 2013 @ 02:24 PM

Hey there,

I know its a really common question and I know all the problems most developers have with robotlegs injections.
and I also know the solutions. But now I dont know what to do.

I have a small class like this.

public class VehicleSpeed extends Signal
    {
        [Inject]
        public var vehicleModel:VehicleModel;
        
        public function VehicleSpeed(identifier:String)
        {
            super();

            this.identifier = identifier;
        }
        
        public override function writeData(value:*):void
        {
            vehicleModel.vehicleSpeed = value;
        }       
    }

the vehicleModel is always null. Why? I cant see any typo, it is not called from the constructor and it is mapped in the main context.
I hope there will be some help anywhere in this universe ;-)

  1. Support Staff 1 Posted by Ondina D.F. on Nov 13, 2013 @ 03:51 PM

    Ondina D.F.'s Avatar

    Hi,

    I'm not sure what writeData() is overriding there and also how and when you call this method. If it's not called in the constructor of VehicleSpeed and the mappings are correct, then the injection should work.

    Have you tried constructor injection like so:

    private var _vehicleModel:VehicleModel;
            
    [Inject]        
    public function set vehicleModel(value:VehicleModel):void
    {
        _vehicleModel = value;
    }
    
    public function get vehicleModel():VehicleModel
    {
        return _vehicleModel;
    }
    

    Or, have you tried to see if the vehicleModel is also null in a posconstruct method?

    [PostConstruct]
    public function someMethod():void
    {
        trace(vehicleModel.vehicleSpeed);
    }
    

    Other than that, it is important how you instantiate VehicleSpeed. Are you doing this:

    private var vehicleSpeed: VehicleSpeed = new VehicleSpeed();

    or this:

    injector.instantiate(VehicleSpeed); instead of injector.getInstance(VehicleSpeed);

    ?

    If so, then please take a look at this discussion to find yet another discussion about injector.instantiate() :P

    http://knowledge.robotlegs.org/discussions/questions/7134-eventmap-...

    Let me know how it goes.
    Ondina

  2. 2 Posted by Chris on Nov 13, 2013 @ 10:32 PM

    Chris's Avatar

    Did you check your context class for errors or omissions?

  3. 3 Posted by iamable on Nov 14, 2013 @ 07:49 AM

    iamable's Avatar

    hi,

    unfortunately [PostConstruct] doesnt work in this class. I dont know why.

    I think I have to tell you more about the structure:

    So I have a Connect class receibing key value pairs from socket:

    private function incomingDataJSON(e:DataEvent):void
            {
                results = JSON.parse(e.data);
                for(var s:String in results)
                {
                    signalList.changeSignal(s, results[s]);
                    deleteDataFromIncomingObject(s);
                }
    }
    

    on data income it is searching through the SingalList holding all the signals (e.g. VehicleSpeed):
    here VehicleSpeed is instantiated with new VehicleSpeed()

    public class SignalList
        {
            private var signalArray:Array = [];
            
            public function SignalList()
            {
                addSignalToList(new VehicleSpeed("Vehicle_Speed"));
            }
            
            private function addSignalToList(signal:Signal):void
            {
                signalArray.push(signal);
            }
            
            public function changeSignal(key:String, value:*):void
            {
                for (var i:int = 0; i < signalArray.length; i++) 
                {
                    if(signalArray[i].identifier == key)
                    {
                        signalArray[i].writeData(value);
                        
                        break;
                    }
                }
            }
    }
    

    and last but not least the above VehicleSpeed extends the Class Signal and write the value from socket to any model:

    public class Signal implements ISignal
        {
            private var _identifier:String = "";
            
            public function Signal()
            {
            }
            
            public function set identifier(value:String):void
            {
                if(value == _identifier) return;
                
                _identifier = value;
            }
    
            public function get identifier():String
            {
                return _identifier;
            }
            
            public function writeData(value:*):void
            {
                trace("please override writeData() in specified Signal " + identifier);
            }   
        }
    
  4. 4 Posted by iamable on Nov 14, 2013 @ 08:23 AM

    iamable's Avatar

    I think the problem is the context class.

    Until now I didnt mapped VehicleSpeed, SignalList and Signal.
    Thatswhy I instantiated them with new.

    I think because of that, injection is not possible and also [PostConstruct doesnt work. Am I right?

    I don't really know how to map those classes.

    In that way?

    injector.mapValue(VehicleSpeed, new VehicleSpeed("Vehicle_Speed"));
    injector.mapSingleton(SignalList);
    

    and then just inject them into the Connect and SignalList?

    Unfortunately this is not working. The instance of VehicleSpeed in SignalList is now null.

    public class SignalList
        {
            private var signalArray:Array = [];
            
            [Inject]
            public var vehicleSpeed:VehicleSpeed;
            
            public function SignalList()
            {
                addSignalToList(vehicleSpeed);
            }
            
            private function addSignalToList(signal:Signal):void
            {
                signalArray.push(signal);
            }
            
            public function changeSignal(key:String, value:*):void
            {
                trace(signalArray[0]);
            }
        }
    
  5. Support Staff 5 Posted by Ondina D.F. on Nov 14, 2013 @ 08:35 AM

    Ondina D.F.'s Avatar

    I'll take a closer look at the pasted code. There might be several issues with it.
    First one I noticed is:

    here VehicleSpeed is instantiated with new VehicleSpeed()

    VehicleSpeed has to be created by the Injector in order to get its dependencies fulfilled.
    With new VehicleSpeed() you create an instance that is in no way known by the Injector.

    Why don't you just inject VehicleSpeed into that class? Or as mentioned in my previous post, you could get an instance like this:

    vehicleSpeed: VehicleSpeed = injector.getInstance(VehicleSpeed);

    But then you'd have to inject the injector into that class as well.

    It would be easier (for me) to find all the problems, if you attached the project or a simplified version of it. Anyway, I'll report back.

  6. Support Staff 6 Posted by Ondina D.F. on Nov 14, 2013 @ 08:36 AM

    Ondina D.F.'s Avatar

    Oh, I see you wrote another post in the meantime:)

  7. 7 Posted by iamable on Nov 14, 2013 @ 08:42 AM

    iamable's Avatar

    Thanks Ondina,
    I tried out several things.

    Now I [Inject] VehicleSpeed.

    public class SignalList
        {
            private var signalArray:Array = [];
            
            [Inject]
            public var vehicleSpeed:VehicleSpeed;
            
            public function SignalList()
            {
            }
            
            [PostConstruct]
            public function initialize():void
            {
                addSignalToList(vehicleSpeed);
            }
            
            private function addSignalToList(signal:Signal):void
            {
                signalArray.push(signal);
            }
            
            public function changeSignal(key:String, value:*):void
            {
                for (var i:int = 0; i < signalArray.length; i++) 
                {
                    if(signalArray[i].identifier == key)
                    {
                        signalArray[i].writeData(value);
                        
                        break;
                    }
                }
            }
        }
    

    your suggestion vehicleSpeed: VehicleSpeed = injector.getInstance(VehicleSpeed); doesnt work.
    injector isnt found. i also dont know why.

    and in VehicleSpeed still the problem ofvehicleModel = null and no possible [PostConstruct]

  8. Support Staff 8 Posted by creynders on Nov 14, 2013 @ 08:55 AM

    creynders's Avatar

    Are you using the RL .swc or compiling from the source files?
    If the latter, you need to add the following compiler argument:

    keep-as3-metadata+=Inject, PostConstruct

  9. 9 Posted by iamable on Nov 14, 2013 @ 09:02 AM

    iamable's Avatar

    i am using the swc

    I found out the problem is this line:

    injector.mapValue(VehicleSpeed, new VehicleSpeed("Vehicle_Speed"));

    if I map a singleton without arguments everything works fine.
    What can I do to map a class with arguments?

  10. Support Staff 10 Posted by Ondina D.F. on Nov 14, 2013 @ 09:34 AM

    Ondina D.F.'s Avatar
    injector.mapSingleton(VehicleModel);
    
    var identifier:String = "something";
    injector.mapValue(String, identifier);
    
    var vehicleSpeed:VehicleSpeed = injector.instantiate(VehicleSpeed); 
    injector.mapValue(VehicleSpeed, vehicleSpeed);
    
    injector.mapSingleton(SignalList);
    

    Does this work?

  11. 11 Posted by iamable on Nov 14, 2013 @ 10:16 AM

    iamable's Avatar

    sorry I dont understand your example.
    I am searching for something like that:

    in Context:

    injector.mapSingleton(VehicleModel);
    injector.mapSingleton(SignalList);
    injector.mapSingleton(VehicleSpeed);
    injector.mapSingleton(ActiveMode);
    

    in SignalList:

    addSignalToList(injector.getInstance(VehicleSpeed("Vehicle_Speed")));
    addSignalToList(injector.getInstance(ActiveMode("CA_ActiveMode")));
    

    but htis is not working

  12. Support Staff 12 Posted by Ondina D.F. on Nov 14, 2013 @ 10:29 AM

    Ondina D.F.'s Avatar
    var identifier:String = "something";
    injector.mapValue(String, identifier);
    

    This will be used for a constructor injection into VehicleSpeed.

    Of course, you can map VehicleSpeed like this:
    injector.mapSingleton(VehicleSpeed);

    and identifier will be injected, as well.

    I gave you the example with injector.instantiate(VehicleSpeed); because you asked how to use mapValue:
    injector.mapValue(VehicleSpeed, new VehicleSpeed("Vehicle_Speed"));

    with constructor arguments:)

    As I said, it would be easier if you attached the relevant code, so I can run it in the debugger.

  13. Support Staff 13 Posted by Ondina D.F. on Nov 14, 2013 @ 10:44 AM

    Ondina D.F.'s Avatar

    Sorry, forgot to say that you need to add [Inject] in your Signal class(btw. can't you rename it??), for the injection to work:

    [Inject]
    public function set identifier(value:String):void
    {
        if(value == _identifier) return;
                
            _identifier = value;
    }
            
    public function get identifier():String
    {
        return _identifier;
    }
    

    See, it's hard to detect all the weak spots in your pasted code. A running example, reproducing your issues, would be the best, and less time consuming:)
    In case you don't want other people to see your code,
    I, or you, can make the discussion private while you're attaching the project, I'll download it, delete it from this discussion, and then mark the discussion as public again.

  14. 14 Posted by iamable on Nov 14, 2013 @ 10:56 AM

    iamable's Avatar

    the problem is the project us much bigger than the part I posted here.
    But I will try to create a small project with these important classes.

    So lets make it private

  15. Support Staff 15 Posted by Ondina D.F. on Nov 14, 2013 @ 10:58 AM

    Ondina D.F.'s Avatar

    It's private now.

  16. 16 Posted by iamable on Nov 14, 2013 @ 11:25 AM

    iamable's Avatar

    ok i created a small project

    everything is working but adding new signals would be really complex.
    I dont know if my structure is the best.

    Very much before i had a simple switch-case in the Connect class for changing data in the model depending on the signal.

    It was working but I want to uncouple the signals. I want just to create a new Class for every signal and add it to the signallist

  17. Support Staff 17 Posted by Ondina D.F. on Nov 14, 2013 @ 11:32 AM

    Ondina D.F.'s Avatar

    ok, I've dl-ed the project and deleted the attachment. I'll take a look at your code
    till later..

  18. Support Staff 18 Posted by Ondina D.F. on Nov 14, 2013 @ 12:53 PM

    Ondina D.F.'s Avatar

    I made just a few changes. It works on my end.
    I'm attaching the app. I'll comment in a few..

    N.B. I changed SignalStructure-app.xml to work with air 3.9 You'll have to change it back to your version

  19. 19 Posted by iamable on Nov 14, 2013 @ 01:43 PM

    iamable's Avatar

    ok thank you. file can be deleted.
    could you please make some explanations for the SignalList and IdentifierVO?

    How does this work for hundreds of signals?

  20. Support Staff 20 Posted by Ondina D.F. on Nov 14, 2013 @ 02:21 PM

    Ondina D.F.'s Avatar

    Yes, just a moment please... Long phone call..I'll be back soon..

  21. Support Staff 21 Posted by Ondina D.F. on Nov 14, 2013 @ 02:45 PM

    Ondina D.F.'s Avatar

    IdentifierVO is just a carrier class. You use it to transport strongly typed data from a place to another.
    If you had something like this:

    var someProperty:String = "something";
    injector.mapValue(String, someProperty);
    

    and then also:

    var anotherProperty:String = "somethingelse";
    injector.mapValue(String, anotherProperty);
    

    and you'd want to inject them both somewhere, the injector would use the last defined rule, so you'd only see the value of anotherProperty in the injected class. To remedy this you'd have to use named injections:

    injector.mapValue(String, someProperty, "someName");
    injector.mapValue(String, anotherProperty, "anotherName");
    

    and inject them like this:

    [Inject (name="someName")]
    public var someProperty:String;
            
    [Inject (name="anotherName")]
    public var anotherProperty:String;
    

    With a VO you can avoid named injections, have someProperty and anotherProperty as properties of the VO and simply read/write the ones you need wherever you need them. If you decide to add or remove a property from the VO or change its data type, the injection itself into a certain class won't suffer. But, if you try to access a non-existent VO property, you get a compiler error immediately.

    How does this work for hundreds of signals?

    I don't know, because I can't understand the use case from the code I have in front of me.
    Why do you need hundreds of signals? Maybe you need just one signal with different payloads? The payloads being also strongly typed VOs.
    Where are you dispatching the signals? Who would listen for them?

  22. Support Staff 22 Posted by Ondina D.F. on Nov 14, 2013 @ 03:05 PM

    Ondina D.F.'s Avatar
  23. Support Staff 23 Posted by Ondina D.F. on Nov 14, 2013 @ 03:11 PM

    Ondina D.F.'s Avatar

    May I mark this discussion as public by now?

  24. 24 Posted by iamable on Nov 14, 2013 @ 03:23 PM

    iamable's Avatar

    Thank you so much for your help this day.

    I found a new solution for my problem.
    instead of passing the identifier into the constructor of every signal and comparing to key of incoming key from socket, I can save the instances of all signals in an dictionary together with the key.

    Now I only have to compare to the keys of the Dictionary and call the method of the related Signal instance.

    public class SignalList
        {
            [Inject]
            public var injector:IInjector;
            
            private var signals:Dictionary = new Dictionary();
            
            public function SignalList()
            {
            }
            
            [PostConstruct]
            public function initialize():void
            {
                signals["Vehicle_Speed"] = injector.getInstance(VehicleSpeed);
                signals["CA_ActiveMode"] = injector.getInstance(ActiveMode);
            }
            
            public function changeSignal(key:String, value:*):void
            {
                if(signals[key]!=null)
                {
                    signals[key].writeData(value);
                }
                else
                {
                    trace("Signal is not used yet");
                }
            }
        }
    
  25. Support Staff 25 Posted by Ondina D.F. on Nov 14, 2013 @ 03:32 PM

    Ondina D.F.'s Avatar

    No problem :)
    Does this mean, we can consider the issue resolved and close the discussion?
    Of course, you can re-open it later, when/if you have more questions, or open new threads for other problems.

  26. 26 Posted by iamable on Nov 14, 2013 @ 03:44 PM

    iamable's Avatar

    yes its problem is solved :)

  27. Ondina D.F. closed this discussion on Nov 14, 2013 @ 03:46 PM.

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