R – Why is the override protected function createChildren being ignored

actionscript-3apache-flex

Here is the error:

    TypeError: Error #1009: Cannot access a property or method of a null object reference.
        at view::ScoreBoard/setChipCount()[C:\Flex Builder 3\StackOverflowQuestion\src\view\ScoreBoard.as:32]
        at model::MainDeckScoreBoard()[C:\Flex Builder 3\StackOverflowQuestion\src\model\MainDeckScoreBoard.as:21]
        at model::MainDeckScoreBoard$cinit()
        at global$init()[C:\Flex Builder 3\StackOverflowQuestion\src\model\MainDeckScoreBoard.as:5]
        at main()[C:\Flex Builder 3\StackOverflowQuestion\src\main.mxml:13]
        at _main_mx_managers_SystemManager/create()
        at mx.managers::SystemManager/initializeTopLevelWindow()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:3188]
        at mx.managers::SystemManager/http://www.adobe.com/2006/flex/mx/internal::docFrameHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:3064]
        at mx.managers::SystemManager/docFrameListener()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\SystemManager.as:2916]

Here is main.mxml:

    <?xml version="1.0"?>
    <mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        creationComplete="initApp()"
    >
        <mx:Script>
            <![CDATA[
                import model.MainDeckScoreBoard;

                public var _mainDeckScoreBoard:MainDeckScoreBoard = MainDeckScoreBoard.instance;

                private function initApp():void {
                    this.addChild(_mainDeckScoreBoard);
                }
            ]]>
        </mx:Script>
    </mx:Application>

Here is MainDeckScoreBoard.as:

package model {
    import view.ScoreBoard;

    [Bindable]
    public dynamic class MainDeckScoreBoard extends ScoreBoard {

        /** Storage for the singleton instance. */
        private static const _instance:MainDeckScoreBoard = new MainDeckScoreBoard( SingletonLock );

        /** Provides singleton access to the instance. */
        public static function get instance():MainDeckScoreBoard {
            return _instance;
        }

        public function MainDeckScoreBoard( lock:Class ) {
            super();
            // Verify that the lock is the correct class reference.
            if ( lock != SingletonLock ) {
                throw new Error( "Invalid Singleton access.  Use MainDeckScoreBoard.instance." );
            }
            this.setChipCount("0");
        }

    } // end class
} // end package

class SingletonLock {
} // end class

Here is ScoreBoard.as:

package view {
    import mx.containers.HBox;
    import view.ScoreBoardLabel;
    import view.ChipCountContainer;
    import view.CardRankList;

    public dynamic class ScoreBoard extends HBox {

        /** The chip count. */
        public var _chipCount:ChipCountContainer;

        public function ScoreBoard() {

            super();

            this.width = 489;
            this.height = 40;
            this.setStyle("horizontalScrollPolicy", "off");
        }

        override protected function createChildren():void {

            super.createChildren();

            if(!_chipCount) {
                _chipCount = new ChipCountContainer();
                this.addChild(_chipCount);
            }
        }

        public function setChipCount(labelText:String):void {
            _chipCount._chipCountLabel.text = labelText;
            invalidateDisplayList();
        }
    }
}

Here is ChipCountContainer.as:

package view {
    import mx.containers.Canvas;

    public dynamic class ChipCountContainer extends Canvas {

        /** The label. */
        public var _chipCountLabel:ChipCountLabel;

        public function ChipCountContainer() {

            super();

            this.width = 20;
            this.height = 20;
        }

        override protected function createChildren():void {

            super.createChildren();

            if(!_chipCountLabel) {
                _chipCountLabel = new ChipCountLabel();
                this.addChild(_chipCountLabel);
            }
        }
    }
}

I've methodically moved things around and waved the invalidate Display List incense while performing a create Children dance but I've only succeeded in completely confusing myself. I've searched the Flex libraries for similar constructions, and it looks OK to me, but I guess I'm just not getting the concept.

Best Answer

I think you're confusing the order of instantiation. Namely, if you want to use setChipCount after the children of the component have been initialized, you should wait for the initialize event to fire, i.e.:

    public dynamic class MainDeckScoreBoard extends ScoreBoard {

    ...

        public function MainDeckScoreBoard( lock:Class ) {
            super();
            // Verify that the lock is the correct class reference.
            if ( lock != SingletonLock ) {
                throw new Error( "Invalid Singleton access.  Use MainDeckScoreBoard.instance." );
            }

            // wait for the children to be created
            addEventListener(FlexEvent.INITIALIZE, onInitialize);
        }

        // executes when the children of this component have been created
        private function onInitialize(event:FlexEvent):void {
            this.setChipCount("0");
        }

    } // end class

For a more detailed explanation of the component instantiation lifecycle, this adobe doc may be helpful:

http://livedocs.adobe.com/flex/3/html/help.html?content=components_06.html