DI instance given undesired null parameter

armoredblimp's Avatar

armoredblimp

10 Apr, 2014 08:18 AM

I'm struggling with an issue I'm encountering in the constructor of a dependency injected object, in robotlegs 2.2.0 (the issue also occurred in 2.1.0).

The object in question is mapped in a configuration:
injector.map(IMyTheme).toSingleton(MyTheme);

The constructor signature of the object is as follows:
public function MyTheme(container:DisplayObjectContainer = null, scaleToDPI:Boolean = true)

When the object is created via dependency injection, the scaleToDPI value is set to false (unexpected!). If I instead take away the DI and straight up create a new MyTheme object with no parameters, the scaleToDPI value is set to true (expected).

I scoured my mappings and I can find no generic mapping for Boolean that may be interfering. I dropped a debug point in the constructor, traced the stack back to the call in line 31 of ConstuctorInjectionPoint and see that this.injectParameters = null.

The problem is that dependency injection is passing a null into the scaleToDPI Boolean param, which, being the Boolean that it is, gets read in as a value of false instead of the expected default of true (thanks ActionScript). Is there a way to instruct dependency injection to, ehrm, not pass in a null?

Aside: I know, it would be simpler to change the constructor signature, to perhaps dontScaleToDPI and flip the internal logic. I probably can do this. Unfortunately this is code I didn't write, I'm just wrapping/using it.

  1. Support Staff 1 Posted by Ondina D.F. on 10 Apr, 2014 09:25 AM

    Ondina D.F.'s Avatar

    Hi,

    The mappings should look like this:

    var scaleToDPI:Boolean = true;
    injector.map(Boolean).toValue(scaleToDPI);
                
    var container:DisplayObjectContainer = new Group();// or whatever
    injector.map(DisplayObjectContainer).toValue(container);
                
    injector.map(IMyTheme).toSingleton(MyTheme);
    

    You need to map both parameters so the constructor injection can occur!

    How are you instantiating MyTheme and where do you pass its arguments, since you haven't created a mapping for them? Could you paste the code for that?

    Ondina

  2. 2 Posted by armoredblimp on 10 Apr, 2014 05:56 PM

    armoredblimp's Avatar

    Hi Ondina,

    I let the DI automatically create the singleton of MyTheme, which I thought was best practice. The creation of this object needs to be deferred until a specific time, needs to be be a singleton, needs to be available to multiple actors via DI.

    I understand what you're telling me, which is to create a parameter mapping & injection, even though I don't "want" one. The default param setting in the constructor is what I want, but the injector is sending nulls. Long story short, your suggestion to make a mapping for scaleToDPI with a value that matches the default should work! Thanks for the awesome suggestion.

    Here's the million dollar stupid question: If I create a mapping for Boolean toValue scaleToDPI, will that injection blindly apply anywhere a generic Boolean is injected? Also I was lead to believe that injection of baseline object classes was not a good practice.

  3. Support Staff 3 Posted by Ondina D.F. on 11 Apr, 2014 08:07 AM

    Ondina D.F.'s Avatar

    Hi,

    I let the DI automatically create the singleton of MyTheme, which I thought was best practice.

    I asked how MyTheme was created, because it wasn't clear to me how you passed its arguments. Many people make the mistake of instantiating a class like this:
    var someClass: SomeClass = SomeClass (someValue, anotherValue); and then expect it to be injected where needed and to have its dependencies satisfied.
    I wanted to know if that was your case too.

    The creation of this object needs to be deferred until a specific time, needs to be be a singleton, needs to be available to multiple actors via DI.

    Yes, injecting the object into the classes that need it is exactly what robotlegs is all about:) But, injection doesn't happen "automagically".
    See these discussions for more details on how injection works (sorry if you already knew all of these) :

    http://knowledge.robotlegs.org/discussions/robotlegs-2/5766-automag...

    http://knowledge.robotlegs.org/discussions/robotlegs-2/8182-the-nec...

    So, with a mapping like yours:
    injector.map(IMyTheme).toSingleton(MyTheme);
    you provided a rule for MyTheme.

    [Inject] public var myTheme: IMyTheme;
    is the injection point, and MyTheme will be lazily instantiated by the Injector, when requested.

    But, with constructor parameters, what you're wanting is that MyTheme is provided with its dependencies, container and scaleToDPI . When the Injector is instantiating MyTheme (in one of the classes where it was injected into) it "sees" container and scaleToDPI in the constructor (the injection point) of MyTheme and it tries to satisfies the request. But, since there is no mapping (rule) for these objects (container and scaleToDPI), the Injector can't inject them into MyTheme and instantiate them. The objects are null!

    I understand what you're telling me, which is to create a parameter mapping & injection, even though I don't "want" one.

    Well, if you don't "want" one, then you shouldn't use constructor parameters;)

    Here's the million dollar stupid question: If I create a mapping for Boolean toValue scaleToDPI, will that injection blindly apply anywhere a generic Boolean is injected? Also I was lead to believe that injection of baseline object classes was not a good practice.

    It is not a stupid question, but I won't refuse the one million dollars, though;)
    That's right mapping primitives like String, Boolean, int will have that effect. That's why it is better to use VOs (strongly typed value objects) instead.
    Another solution would be "named" injection:

    http://knowledge.robotlegs.org/discussions/questions/7338-injected-...

    http://knowledge.robotlegs.org/discussions/robotlegs-2/9800-injecti...

    I prefer to use VOs!

    You said that you can't modify the code for MyTheme, right?
    It is still unclear to me how the parameters of MyTheme are used in your app.
    Can you tell more about that? Maybe we can find an easier/better solution for your use case.

    Ondina

  4. 4 Posted by armoredblimp on 11 Apr, 2014 05:45 PM

    armoredblimp's Avatar

    As is often the case a solution involves a compromise, and there are plenty of options for me to go with here. I will probably go with named injection.

    I was essentially tripped up by ActionScript interpreting an undefined Boolean as false (not null), which is something that Javascript does not do.

    Thanks again Ondina for the most excellent, bend-over-backwards explanations!

  5. armoredblimp closed this discussion on 11 Apr, 2014 05:45 PM.

  6. creynders re-opened this discussion on 12 Apr, 2014 06:30 AM

  7. Support Staff 5 Posted by creynders on 12 Apr, 2014 06:30 AM

    creynders's Avatar

    Just my 2 cents: IMO the best solution is to create a proxy/wrapper for MyTheme which wraps all methods of MyTheme and accepts a VO in the constructor which it uses to create a MyTheme instance. Mapping primitives is a definite no-no and named injections ... well yeah, they're pretty bad too.

  8. Support Staff 6 Posted by creynders on 12 Apr, 2014 06:34 AM

    creynders's Avatar

    Or if you don't mind an extra dependency, you could use a factory.

    --
    Sent from Gmail Mobile

  9. Ondina D.F. closed this discussion on 05 Jun, 2014 04:39 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