Null error on sub-view in Command

jason.merrill's Avatar

jason.merrill

15 Feb, 2011 06:21 PM

This problem isn't exactly complex, just need to explain it in parts. This is my second HelloWorld app for learning purposes, it's not a real app.

So, I have a command which references an injected view:

  public class MoveStuffCommand extends Command
{
    [Inject]
    public var compositeShapeView:CompositeShapeView;

    public function MoveStuffCommand()
    {
        super();
    }

    override public function execute():void
    {
        compositeShapeView.squareView.x += 10;
        compositeShapeView.squareView.y -= 10;
    }

As you see in the code above, in the execute method, I take action on a sub-view:

    compositeShapeView.squareView.x += 10;

In my Context, the compositeShapeView is injected fine, from the startup method:

    var compositeShapeView:CompositeShapeView = new CompositeShapeView();
    injector.mapValue(CompositeShapeView, compositeShapeView);

At runtime, the command references the CompositeShapeView instance fine evidenced by traces, but not the sub-view (squareView) - that's null for some reason. The squareView property is a public property which is set inside the constructor of that sprite:

      public class CompositeShapeView extends Sprite
{
    public var squareView:SquareView;

   public function CompositeShapeView()
    {
        super();

        var squareView:SquareView = new SquareView();

        addChild(squareView);

    }
  }

Why would I get null errors on squareView in the command? The command is run when an object is clicked, so the constructor in SquareView must have already run long before. Or, if there is a better way to do this architecturally in Robotlegs, I'm all ears.

Any thoughts are greatly appreciated.

Jason Merrill

  1. 1 Posted by Stray on 15 Feb, 2011 06:29 PM

    Stray's Avatar

    Dude - this is a nice easy one...

    var squareView:SquareView = new SquareView();

    Should really be

    squareView = new SquareView();

    You're assigning it to a local property instead of the class member variable.

    Oops! (easily done)

    That said... injecting views into commands isn't really the conventional approach. The norm would be to pick up the event in a mediator for view stuff. Commands are really for model / service access.

    But, for now, your bug is fixable!

  2. 2 Posted by jason.merrill on 15 Feb, 2011 06:33 PM

    jason.merrill's Avatar

    Ah! Duh, you're right, stupid mistake. I had been mixing code and forgot to switch it back, sloppy on my part.

    >> injecting views into commands isn't really the conventional approach.
    >>The norm would be to pick up the event in a mediator for view stuff.
    >>Commands are really for model / service access.

    OK - good to know! I had used Commands in Cairngorm previously, and had done a lot of view stuff in them. Need to rewire my brain to the Robotlegs way of thinking, thanks again!

     Jason Merrill
     Instructional Technology Architect
     Bank of America Global Learning

    _______________________

    -----Original Message-----
    From: Stray [mailto:[email blocked]]
    Sent: Tuesday, February 15, 2011 1:29 PM
    To: Merrill, Jason
    Subject: Re: Null error on sub-view in Command [Problems]

  3. 3 Posted by Stray on 15 Feb, 2011 07:10 PM

    Stray's Avatar

    Cool - no problem - I find those kinds of things the hardest to debug when it's my own code.

    The usual approach would be that the mediator for the parent view picks up the event and then runs api on the parent view - which could be a view-controller that contains view-stuff, or might be a composite that has view-controllers to pass off functionality to if the same functionality repeats in various places.

    The basic idea behind it is that you shouldn't mix your framework with your view, because then your view needs your framework in order to run. It's very much possible to achieve most view stuff in the view layer, and then only use the robotlegs classes to join your view to the application (and thus other views as well).

    Stray

  4. 4 Posted by jason.merrill on 15 Feb, 2011 07:17 PM

    jason.merrill's Avatar

    OK cool. So how would you recommend, from the parent mediator (i.e. CompositeShapeViewMediator), I reference and listen to the sub-views (i.e. squareView) inside of compositeShapeView for example? Would I inject an instance of CompositeShapeView inside of CompositeShapeViewMeditator?

     //CompositeShapeViewMediator.as
    
     [Inject]
     public var compositeShapeView:CompositeShapeView;
    
     //then when the event is heard:
     compositeShapeView.squareView.x += 10;
    

    so, for example,

     package landingPage.mediators 
     {
         import landingPage.events.BallViewEvent;
         import landingPage.view.CompositeShapeView;
         import org.robotlegs.mvcs.Mediator;
    
         public class CompositeShapeViewMediator extends Mediator
         {
                [Inject]
                public var compositeShapeView:CompositeShapeView;
    
             public function CompositeShapeViewMediator()
             {
                    super();
    
                 compositeShapeView.ballView.addEventListener(BallViewEvent.BALL_VIEW_CLICKED, onBallViewClicked)
             }
    
             private function onBallViewClicked(event:BallViewEvent):void
             {
                 compositeShapeView.squareView.x += 10;
             }
         }
     }
    

    If so, didn't you say yesterday that you wouldn't normally inject views? I saw a reference in the Robotlegs documentation that had an injection of a view into a mediator, but is this not recommended?

    And the code above, doesn't seem to work - the instance of compositeShapeView is null, even though in my Context I have:

     var compositeShapeView:CompositeShapeView = new CompositeShapeView();
     injector.mapValue(CompositeShapeView, compositeShapeView);
    

    Jason Merrill

  5. 5 Posted by Stray on 15 Feb, 2011 07:58 PM

    Stray's Avatar

    Hi Jason - basically the only view injection that I'd normally do is the injection of the view into its mediator.

    I'm not really clear enough on what you're trying to achieve here -

    where does the action initiate? What is it that the user has clicked? Is it a button somewhere?

    Normally actions on sub views would be taken by the parent view.

    So you might expose an API like moveItem(.... ) on the compositeShapeView.

    Can you explain a bit more about your process?

  6. 6 Posted by jason.merrill on 15 Feb, 2011 08:22 PM

    jason.merrill's Avatar

    basically the only view injection that I'd normally do is the injection of the view into its mediator

    Yes, and that is what I am trying to do here. This is just a test HelloWorld app, but basically, the user clicks on ballView, which dispatches BallViewEvent.BALL_VIEW_CLICKED. Both ballView and squareView are children of CompositeShapeView.

    When BallViewEvent.BALL_VIEW_CLICKED is heard, it should move the squareView 10 pixels.

    The problem right now, is that the compositeShapeView is, for some reason I cannot explain, null inside of the instance of CompositeShapeViewMeditator class where it is injected. My context looks like this (snippet)

          mediatorMap.mapView(CompositeShapeView, CompositeShapeViewMediator);
    
          var compositeShapeView:CompositeShapeView = new CompositeShapeView();
            injector.mapValue(CompositeShapeView, compositeShapeView);
    

    The mediator is as I have shown in the previous post. Any ideas why compositeShapeView is coming up null?

  7. Support Staff 7 Posted by Stray on 15 Feb, 2011 08:47 PM

    Stray's Avatar

    You don't need the mapValue mapping.

    Just do

    mediatorMap.mapView(CompositeShapeView, CompositeShapeViewMediator);
    

    and when your CompositeShapeView hits the stage, the mediator will be created for it and, assuming you've got something like

    [Inject]
    public var view:CompositeShapeView
    

    inside your mediator, it should be injected.

    You should be aware that injection happens after the constructor runs (There is a big list of 'common problems' that is linked to on the main page for stuff like this by the way - really worth a read).

    So - while it'll always be null in the constructor, it should be there when onRegister runs.

    The problem you're having is partly that there is no need to pass events of the nature you're describing through the robotlegs framework. It doesn't make sense. You would just listen for ballView being clicked in the CompositeShapeView and then run a function to move the square view...

    Generally, robotlegs only helps to wire together parts of the view that are separate - for example if this was a drawing-application, and the user changed the colour of the current selected item, you'd send a ColourEvent.COLOUR_CHANGED event and that might get picked up by the main drawing stage area.

    I think you're going to really struggle to get a good understanding of robotlegs working with view-related code only. That's not where it's intended to help you.

    Let's assume that you've got a menu with 4 buttons that can nudge something up, down, left or right, and both the ball and the square on the stage, and that it's possible for either the ball or the square to be the 'currently selected item' in the CompositeShapeView.

    We want the current selected item to be nudged in the right direction when the user clicks one of the nudge buttons.

    To achieve this, we might have the following classes:

    NudgeMenu
    NudgeMenuMediator
    NudgeEvent
    CompositeShapeView
    CompositeShapeViewMediator
    

    With code snippets like this:

    NudgeMenu:

    protected function listenForNudgeClicks():void
    {
        btnLeft.addEventListener(MouseEvent.CLICK, createNudgeHandler(-10, 0))
        btnRight.addEventListener(MouseEvent.CLICK, createNudgeHandler(10, 0))
        btnUp.addEventListener(MouseEvent.CLICK, createNudgeHandler(0, -10))
        btnDown.addEventListener(MouseEvent.CLICK, createNudgeHandler(0, 10))
    }
    
    protected function createNudgeHandler(xNudge:Number, yNudge:Number):Function
    {                                                
        var handler:Function = function(e:MouseEvent):void {
                var e:NudgeEvent = new NudgeEvent(NudgeEvent.NUDGE_REQUESTED, xNudge, yNudge); 
                dispatchEvent(e);
            }   
    
        return handler;
    
    }
    

    NudgeMenuMediator:

    public override function onRegister():void
    {                                                         
        // this just redispatches the event to the whole application
        addViewListener(NudgeEvent.NUDGE_REQUESTED, dispatch);
    }
    

    CompositeShapeViewMediator:

    public override function onRegister():void
    {
        addContextListener(NudgeEvent.NUDGE_REQUESTED, passNudgeToView);
    }                                                                   
    
    protected function passNudgeToView(e:NudgeEvent):void
    {
        compositeShapeView.nudgeCurrentSelectionBy(e.x, e.y);
    }
    

    CompositeShapeView

    public function nudgeCurrentSelection(xNudge:Number, yNudge:Number):void
    {
        _currentSelectedShape.x += xNudge;
        _currentSelectedShape.y += yNudge;
    }
    

    Does that make a bit more sense? Here we're using the robotlegs framework to decouple the nudge controls from the subject of the nudges.

    HelloWorld is always a bit difficult because in the real world nobody would use a framework for a HelloWorld size application!

    Stray

  8. 8 Posted by jason.merrill on 15 Feb, 2011 09:58 PM

    jason.merrill's Avatar

    Thanks, no I got it - I am only listening to view events like Click and staying within views. The other events were only to test out the RL framework and how events pass around.

    This morning I thought Commands were used on views as well, but I see now how they generally wouldn't be.

    Thanks for your detailed response. The key was what you mentioned about the injection happening after the constructor runs. I put the necessary code in an

      override public function onRegister():void
     {
     }
    

    method in the mediator and it's working fine now. Thanks for your help! Great stuff, I've got a lot to go on now. I already have some model code working in RL and that was pretty straightforward - so my questions were really about working with views correctly.

  9. Support Staff 9 Posted by Stray on 16 Feb, 2011 04:48 PM

    Stray's Avatar

    Great - glad you're sorted :)

    Stray

  10. Stray closed this discussion on 16 Feb, 2011 04:48 PM.

  11. jason.merrill re-opened this discussion on 16 Feb, 2011 05:38 PM

  12. 10 Posted by jason.merrill on 16 Feb, 2011 05:38 PM

    jason.merrill's Avatar

    Thanks yeah - so let me ask one more question if I may. If you wanted a view to respond to changes in the model, you would inject a singleton of the model into the view's mediator, and then listen to the event on that model, and create a handler to alter the view, right? That's my MVC way of thinking, but would you instead use a Command?

     Jason Merrill
     Instructional Technology Architect
     Bank of America Global Learning

    _______________________

    -----Original Message-----
    From: Stray [mailto:[email blocked]]
    Sent: Wednesday, February 16, 2011 11:49 AM
    To: Merrill, Jason
    Subject: Re: Null error on sub-view in Command [Problems]

  13. Support Staff 11 Posted by Stray on 16 Feb, 2011 05:48 PM

    Stray's Avatar

    Hi Jason - robotlegs provides the shared eventDispatcher - which is injected into any class that extends Actor and into Mediators.

    So - your model would dispatch the event on the shared eventDispatcher, and the mediator would just listen for it on the shared eventDispatcher.

    You don't need to inject the model into the mediator.

    You should really check out some of the demos that are around - look in the Examples section of the forum and you'll get a feel for it.

    Thanks,

    Stray

  14. 12 Posted by jason.merrill on 16 Feb, 2011 05:54 PM

    jason.merrill's Avatar

    Thanks - yeah, been through a lot of the demos - I just forget easily. :) Thanks.

     Jason Merrill
     Instructional Technology Architect
     Bank of America Global Learning

    _______________________

    -----Original Message-----
    From: Stray [mailto:[email blocked]]
    Sent: Wednesday, February 16, 2011 12:48 PM
    To: Merrill, Jason
    Subject: Re: Null error on sub-view in Command [Problems]

  15. Stray closed this discussion on 18 Feb, 2011 07:00 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