Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1] 2
Print
Author Topic: View component is null during startup?  (Read 17123 times)
codecraig
Full Member
***
Posts: 23


View Profile Email
« on: August 05, 2008, 11:55:05 »

I have the following setup:

StartupCommand creates ApplicationMediator
ApplicationMediator creates FooMediator (and passes it view.FooView)

The FooMediator constructor looks like:
:
public function FooMediator(viewComponent:Object=null) {
      super(NAME, viewComponent);
      view.dummyButton.addEventListener(....);
}

private function get view():FooView {
   return viewComponent as FooView;
}

I get the following on the "view.dummButton.addEventListener" line:
Cannot access a property or method of a null object reference.

In the debugger I see that "dummyButton" is null.

The FooView looks like:
:
<mx:Panel ....>
<mx:Button id="dummyButton" label="Close" />
</mx:Panel>

Any ideas?
Logged
ramanuja
Jr. Member
**
Posts: 17


View Profile Email
« Reply #1 on: August 05, 2008, 12:28:42 »

Hi,

If you are using the MultiCore version of PureMVC, then my first advice is to put the addEventListener() code into the onRegister() method of the mediator instead of the constructor.

Seeing the code snippets that you have provided, I could not find any problem. You said you were passing view.FooView to the FooMediator constructor. So FooView is the 'id' of the child in view object?

You said view.dummyButton is null. Did you check whether view itself is null?

You may also want to check whether the viewComponent is getting set properly in ApplicationMediator.

Ramanuja.
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #2 on: August 05, 2008, 01:32:38 »

I am using the standard version.

FooView is a child of the "view".

view.dummyButton is null but "view" is not (as i could see in the debugger).

Not really sure what the issue is.  Should I register the view.dummyButton.addEventListener somewhere else besides the constructor?  I think it's ok to do it in the constructor since I've done it elsewhere and the samples do it.

thanks.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #3 on: August 05, 2008, 02:19:27 »

FooView is a child of the "view".

Then you're doing your cast wrong:

:
private function get view():FooView {
   return viewComponent as FooView;
}

This code expects that view is FooView, not a child of FooView.

So what is view? Lets say view is actually BarView. Then you should have:

:
private function get view():BarView{
   return viewComponent as BarView;
}

private function get fooView():FooView {
   return view.fooView as FooView;
}

private function get dummyButton():Button{
   return fooView.dummyButton as Button;
}

That should get you there, but it's a little invasive. You really don't the Mediator knowing that much about the internals of the view component. You could put the listener on the view component if the event bubbles. If not, catch it in the view component and redispatch.

Check out how the EmployeeAdmin demo handles this this in its RolePanel and RolePanel Mediator:

RolePanel:
http://puremvc.org/pages/demos/AS3/Demo_AS3_Flex_EmployeeAdmin/srcview/source/org/puremvc/as3/demos/flex/employeeadmin/view/components/RolePanel.mxml.html

RolePanelMediator:
http://puremvc.org/pages/demos/AS3/Demo_AS3_Flex_EmployeeAdmin/srcview/source/org/puremvc/as3/demos/flex/employeeadmin/view/RolePanelMediator.as.html

Note that the RolePanelMedator does take a shortcut and knows about the RolePanel combo, but it shouldn't.

-=Cliff>
« Last Edit: August 05, 2008, 02:36:45 by puremvc » Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #4 on: August 05, 2008, 02:56:41 »

Sorry for the mix up.

I originally stated that the ApplicationMediator passes the FooView to the FooMediator by doing view.FooView

Application.mxml
:
<mx:application...>
    ...
    <components:FooView id="FooView" />
    ...
</mx:application>

ApplicationMediator.as
:
public function ApplicationMediator(...) {
   ...
   facade.registerMediator(new FooMediator(view.FooView));
}

private function get view():Application {
      return viewComponent as Application;
}

So I am passing in a FooView.
Logged
ramanuja
Jr. Member
**
Posts: 17


View Profile Email
« Reply #5 on: August 05, 2008, 09:20:00 »

Hi,

I tried the same setup as yours and it worked for me! Hmmm.. No ideas for now  ???

Ramanuja.
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #6 on: August 06, 2008, 08:19:04 »

So I followed he helpful information (for ViewStack at least): http://forums.puremvc.org/index.php?topic=280.0

However I still have the same problem.

ApplicationMediator
:
public function ApplicationMediator(viewComponent:Object=null)
{
super(NAME, viewComponent);

view.addEventListener(AppMain.EVT_SHOW_FOO, onShowFoo);
view.addEventListener(AppMain.EVT_SHOW_BAR, onShowBar);
checkForMediator(view.currentViewSelector, view.activeView);
}
protected function onShowFoo(e:Event):void {
view.currentViewSelector = AppMain.VIEW_FOO;
checkForMediator(AppMain.VIEW_FOO, view.activeView);
sendNotification(ApplicationFacade.FOO_MODE);
}
protected function onShowBar(e:Event):void {
view.currentViewSelector = AppMain.VIEW_BAR;
checkForMediator(AppMain.VIEW_BAR, view.activeView);
sendNotification(ApplicationFacade.BAR_MODE);
}
        protected function checkForMediator( childSelector:int, child:Object ):void {
        switch (childSelector) {
        case AppMain.VIEW_FOO:
        if (facade.retrieveMediator(FooViewMediator.NAME) == null) {
        facade.registerMediator(new FooViewMediator(child));
        }
        break;
        case AppMain.VIEW_BAR:
if (facade.retrieveMediator(BarViewMediator.NAME) == null) {
        facade.registerMediator(new BarViewMediator(child));
        }
        break;
        }
        }

AppMain.mxml
:
<mx:Script>
<![CDATA[
import mx.events.ItemClickEvent;
import mx.rpc.events.FaultEvent;

public static const EVT_SHOW_FOO:String = "showFoo";
public static const EVT_SHOW_BAR:String = "showBar";

public static const VIEW_FOO:int = 0;
public static const VIEW_BAR:int = 1;

[Bindable] public var currentViewSelector:int = VIEW_FOO;
public var activeView:Object = viewEvents;

private var facade:ApplicationFacade = ApplicationFacade.getInstance();

private function init():void {
activeView = viewStack.selectedChild;
facade.startup(this);
}

private function submit(action:String):void {
dispatchEvent(new Event(action, true));
}
]]>
</mx:Script>

<mx:Binding source="viewStack.selectedChild" destination="activeView" />

<mx:ApplicationControlBar id="appControlBar" width="65%" dock="true">
<mx:Button id="buttonFoo" label="Foo" click="submit(EVT_SHOW_FOO)" />
<mx:Button id="buttonBar" label="Bar" click="submit(EVT_SHOW_BAR)" />
</mx:ApplicationControlBar>

<mx:ViewStack id="viewStack" width="100%" height="75%"
selectedIndex="{currentViewSelector}">

<components:FooView id="viewFoo" width="100%" height="100%" />

<components:BarView id="viewBar" width="100%" height="100%" />
</mx:ViewStack>

So when the app first starts up you see the "FooView".  When I click on the "Bar" button I get the NULL reference error in my BarViewMediator when I try to set the dataProvider on my "listBarObjects":
BarViewMediator.as
:
public function BarViewMediator(viewComponent:Object=null) {
super(NAME, viewComponent);

var data:ArrayCollection = new ArrayCollection();
for (var i:int = 0; i < 5; i++) {
var q:BarVO = new BarVO ();
q.name = "Bar " + (i+1);
q.msg = "something random here - " + i;
data.addItem(q);
}

view.listBarObjects.dataProvider = data;
}

Did I miss something about how to handle this deferred instantiation?  When I debug I can see that the "checkForMediator" method gets called when I click the "Bar" button and I see it creates a new "BarViewMediator" and passes it the "BarView" object, however the "listBarObjects" is null.
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #7 on: August 06, 2008, 08:24:20 »

Well instead of having my BarViewMediator do:

:
view.listBarObjects.dataprovider = data;

I instead put a bindable ArrayCollection in my BarView and bound the dataProvider of the list to it.

:
[Bindable] public var data:ArrayCollection = new ArrayCollection();
...
...
<mx:List id="listBarObjects" dataProvider={data}" />

Now in my BarViewMediator I have:

:
view.data = data;

and it works.
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #8 on: August 06, 2008, 11:53:12 »

Well I got that working but now I have the same in a slightly different way.

I have setup my code similar to the EmployeeAdmin demo.  So I have a PersonForm and PersonList in say "PersonView".  I also have "LoginView".  The LoginView and PersonView are in a ViewStack.  I have taken the steps (as shown earlier, in a slightly different example) to handle registering the mediator later.

However, in my PersonFormMediator the constructor gets a "null" view component.

The ApplicationMediator is doing the following in the checkForMediator:
:
        case AppMain.VIEW_PERSON:
if (facade.retrieveMediator(PersonFormMediator.NAME) == null) {
var personView:PersonView = child as PersonView ;
        facade.registerMediator(new PersonFormMediator(personView.personForm));
        }

For some reason the "personForm" is null.  Any ideas why??
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #9 on: August 06, 2008, 05:37:02 »

Search these forums for 'deferred instantiation', you'll find the answer(s).

-=Cliff>
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #10 on: August 07, 2008, 03:57:01 »

I did once but it would seem I haven't looked hard enough.

Gotta say a lot of the posts that come up have the end result of "search for deferred instantiation, you'll find the answer(s)".

 :)

Maybe there needs to be a "sticky" post that gives the answer in one place.
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #11 on: August 07, 2008, 04:11:37 »

AppMain.mxml
 - ApplicationControlBar (has two buttons to change the viewstack)
 - a ViewStack with 2 views, EmployeeView and PersonView
 - has a mediator, AppMediator

PersonView.mxml
 - PersonForm (has a mediator)
 - PersonList (has a mediator)

The AppMediator gets called when the "Employee" or "Person" buttons are clicked (they toggle which view is shown in the ViewStack).  When the AppMediator gets the call it will call the "checkForMediator" function which works like:

:
protected function checkForMediator( childSelector:int, child:Object ):void {
    switch (childSelector) {
        case AppMain.VIEW_EMPLOYEES:
            if (facade.retrieveMediator(EmployeeViewMediator.NAME) == null) {
                facade.registerMediator(new EmployeeViewMediator(child));
            }
            break;
        case AppMain.VIEW_PERSON:
            if (facade.retrieveMediator(PersonFormMediator.NAME) == null) {
                var view:PersonView = child as PersonView;
                facade.registerMediator(new PersonFormMediator(view.personForm));
            }
            if (facade.retrieveMediator(PersonListMediator.NAME) == null) {
                var view:PersonView = child as PersonView;
                facade.registerMediator(new PersonListMediator(view.personList));
            }
            break;
    }
}

When the application first loads the EmployeeView is shown.  If click the "Person" button in the ApplicationControlBar then the PersonView should be shown.  However, when that action occurs the PersonFormMediator gets a null reference error because in its constructor it tries to do:

:
personForm.addEventListener(....);  // personForm is the PersonForm view

The suggestion here (http://forums.puremvc.org/index.php?topic=280.msg1200#msg1200) says to add an "onCreationComplete" event to the view component.  But how can my PersonFormMediator do that when the view object it is given is null?

Should I add the event listener from the AppMediator so that it listens for "onCreationComplete" from the "PersonView" and then when it does get the event, then create the PersonFormMediator and PersonListMediator?
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #12 on: August 07, 2008, 04:21:28 »

Ok so I updated AppMediator to look like this:

:
protected function checkForMediator( childSelector:int, child:Object ):void {
    switch (childSelector) {
        case AppMain.VIEW_EMPLOYEES:
            if (facade.retrieveMediator(EmployeeViewMediator.NAME) == null) {
                facade.registerMediator(new EmployeeViewMediator(child));
            }
            break;
        case AppMain.VIEW_PERSON:
if (facade.retrieveMediator(PersonFormMediator.NAME) == null ||
facade.retrieveMediator(PersonListMediator.NAME) == null) {
var personView:PersonView = child as PersonView;
personView.addEventListener(FlexEvent.CREATION_COMPLETE, onPersonViewCreationComplete);
}
            break;
    }
}

private function onPersonViewCreationComplete(event:FlexEvent):void {
if (facade.retrieveMediator(PersonFormMediator.NAME) == null) {
var personView:PersonView = event.target as PersonView;
facade.registerMediator(new PersonFormMediator(personView.personForm));
}
if (facade.retrieveMediator(PersonListMediator.NAME) == null) {
var personView:PersonView = event.target as PersonView;
facade.registerMediator(new PersonListMediator(personView.personList));
}
}

That seems to work, just want to verify that is the suggested practice, is it?
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #13 on: August 07, 2008, 04:26:17 »

1) I have a FAQ underway. Soon come.
 
2) Add the onCreationComplete handler to the MXML of the component. When it has been constructed it will send your event, which will bubble up through the view hierarchy(bubbles=true) and should be picked up by an event listener placed on the parent by its mediator. Let that mediator instantiate the new mediator passing it a reference to event.target.

-=Cliff>
Logged
codecraig
Full Member
***
Posts: 23


View Profile Email
« Reply #14 on: August 07, 2008, 05:16:45 »

In response to #2.

So you are saying that the PersonView should fire an event once it's "onCreatationComplete" has been called right?

PersonView.mxml
:
<mx:Panel ... creationComplete="notify(event)" />

private function notify(event:FlexEvent):void {
      event.bubbles=true;
      dispatchEvent(event);
}

Then have the AppMediator (in this case) register for the "FlexEvent.CREATION_COMPLETE" for the "PersonView"?

Seems to be the same as what I already did except I removed the step of having the view catch the event and fire it.
Logged
Pages: [1] 2
Print