1

I have a View class EmployeeList as follows:

<?xml version="1.0" encoding="utf-8"?>
<s:NavigatorContent xmlns:fx="http://ns.adobe.com/mxml/2009" 
                    xmlns:s="library://ns.adobe.com/flex/spark"
                    xmlns:parsley="http://www.spicefactory.org/parsley"
                    xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300">


    <fx:Script>
        <![CDATA[
            import cafeparsley.model.EmployeeListPM;

            [Inject]
            [Bindable]
            public var model : EmployeeListPM;

            [Init]
            public function init () : void {
                model.init();
            }


        ]]>
    </fx:Script>    

    <s:Panel title="Employee List" horizontalCenter="0">
        <s:HGroup paddingTop="50">
            <s:Button label="Add New Employee" click="model.addNewEmployee()" />
            <mx:Spacer width="100%" />
            <s:Button label="Logout" click="model.logout()" />
            <mx:Spacer width="100%" height="20" />  
        </s:HGroup>
        <s:List id="empList" dataProvider="{ model.employees }" labelFunction="model.properName" 
                 change="model.initUpdateEmployee(empList.selectedItem);empList.selectedIndex = -1;" width="100%" />
        <s:Label id="error" color="0xFF0000" />
    </s:Panel>  

</s:NavigatorContent>

The PM looks like this:

package cafeparsley.model
{
    import cafeparsley.events.EmployeeEvent;
    import cafeparsley.events.NavigationEvent;
    import cafeparsley.services.impl.EmployeeServiceImpl;
    import cafeparsley.vo.Employee;

    import flash.events.EventDispatcher;

    import mx.collections.ArrayCollection;
    import mx.rpc.IResponder;

    [Bindable]
    [Event(name="navigationEvent", type="cafeparsley.events.NavigationEvent")]
    [ManagedEvents("navigationEvent")]
    public class EmployeeListPM extends EventDispatcher implements IResponder
    {

        public var employeeService : EmployeeServiceImpl = new EmployeeServiceImpl();

        public var employees : ArrayCollection;

        public function init() : void
        {
            loadEmployees();
        }


        public function EmployeeListPM() 
        {
        }

        public function loadEmployees():void
        {
            employeeService.loadEmployees( this );
        }  

Regardless of whether I use or autowiring to perform injection, when I run this I get a the following error message:

TypeError: Error #1009: Cannot access a property or method of a null object reference. at cafeparsley.view::EmployeeList/_EmployeeList_List1_i()[C:\dev\code\workspace\Examples\CafeParsley\src\cafeparsley\view\EmployeeList.mxml:29] at cafeparsley.view::EmployeeList/_EmployeeList_Array2_c() at mx.core::DeferredInstanceFromFunction/getInstance()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\DeferredInstanceFromFunction.as:105] at spark.components::SkinnableContainer/createDeferredContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:985] at spark.components::SkinnableContainer/createContentIfNeeded()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:1014] at spark.components::SkinnableContainer/createChildren()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:827] at mx.core::UIComponent/initialize()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:7349] at mx.core::UIComponent/http://www.adobe.com/2006/flex/mx/internal::childAdded()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:7241] at mx.core::UIComponent/addChildAt()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:6947] at spark.components::Group/addDisplayObjectToDisplayList()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:1825] at spark.components::Group/http://www.adobe.com/2006/flex/mx/internal::elementAdded()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:1416] at spark.components::Group/setMXMLContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:512] at spark.components::Group/set mxmlContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\Group.as:452] at spark.components::SkinnableContainer/set mxmlContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:604] at spark.components::SkinnableContainer/createDeferredContent()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:986] at spark.components::SkinnableContainer/createContentIfNeeded()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:1014] at spark.components::SkinnableContainer/createChildren()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\SkinnableContainer.as:827] at spark.components::NavigatorContent/createChildren()[E:\dev\4.x\frameworks\projects\spark\src\spark\components\NavigatorContent.as:225] at mx.core::UIComponent/initialize()[E:\dev\4.x\frameworks\projects\framework\src\mx\core\UIComponent.as:7349] at cafeparsley.view::EmployeeList/initialize()

So the employeeListPM is null when the error is thrown. However if I comment out the <s:List> component, rerun and set a breakpoint in the init method, init() will get called. So it's not that my context config is wrong, it's just that the PM hasn't been injected in time and the error is thrown. But according to the Parsley manual if I use autowiring or <parsley:configure/> the PM should be injected by the time it is needed.

I can't see what is I am doing wrong in what I thought was a relatively trivial dependency injection scenario. Can you help?

Murray
  • 11
  • 1
  • 3

2 Answers2

1

A couple of points here:

Your example doesn't include the <Configure /> or <FastInject /> tags, however since you mention them in your post I'll assume that they're just missing from the sample code. (If not, then you need to add one of these for this to work).

However, more likely, you have race conditions in your code.

Specifically, these lines:

labelFunction="model.properName" 
change="model.initUpdateEmployee(empList.selectedItem);empList.selectedIndex = -1;"

Model is an injected property, however it's not guaranteed to have been injected at the time that the code is first run.

Instead, move the code into script within the class which performs a null check, and then defers logic back to the PM.

ie:

labelFunction="nameFunction"


private function nameFunction(item:Object):String
{
    return (model) ? model.properName(item) : "";
}
Marty Pitt
  • 28,822
  • 36
  • 122
  • 195
0

Another thing to consider is that you are calling model.init() from the View's function marked with the Parsley [Init] metatag. I would suggest that you apply the same [Init] metatag to the init() method of the Model.

[Init]    
public function loadEmployees():void {
     employeeService.loadEmployees( this );
}

Do this rather than calling model.init() from the view. Altho the Lifecycle documentation states:

The methods marked with [Init] get invoked after the object has been instantiated and all injections have been processed.

I've had much more consistent results not calling any init() methods between injected objects directly but rather utilizing the metadata tag approach.

Pelted
  • 81
  • 4