10 Replies Latest reply on Jun 22, 2010 9:41 AM by rtalton

    Component instantiation before xml parsing complete

    jprzybylowsk Level 1

      Hi there!

       

      I have a custom component based upon a Canvas that contains a number of Accordion menus. The data model for the component is created when an XML file is loaded and parsed. This works locally without issue. When on a testing server, however, my application apparently attempts to create my custom component before the model has been created and throws a null value error (The model is bound to my component in the mxml properties).

       

      Wondering if anyone has some advice to offer with regard to this issue? Can you suggest a practical way to defer the instantiation of my custom component until I am sure my model is instatiated and contains data?

       

      I've changed my code to load the xml data for my model in the preinitialize phase of the application and this seems to work but I'm not confident that's the proper solution and will work predictably. If I can blather further, this also brings up another question--when does the preinitialize phase end? If I call an HTTPService.send method during this time am I effectively pausing the application while I wait for the data to return? It's very confusing to me.

       

      Thanks in advance for any/all help offered!

       

      Joel P.

        • 1. Re: Component instantiation before xml parsing complete
          rtalton Level 4

          I'll take a stab at this. If you have a simple parent along with a custom component set up using MXML, you can try this:

           

          In your custom component, make sure your data model is bindable and declared as public, like (here I'm assuming you are using an Array Collection):

          [Bindable]

          public var componentDataAC:ArrayCollection;

           

          In the main application, your data being returned is assigned to a var, like:

           

          //declare your var:

          [Bindable]

          private var myDataAC:ArrayCollection = new ArrayCollection();

           

          //HTTPService result handler:

          private function resultHandler(event:ResultEvent):void{

               myDataAC =  event.result as ArrayCollection;

          }

           

          Now in the MXML declaration for the component, simply assign this data to your component's data var:

          <myComps:MyCustomComponent

               componentDataAC="{myDataAC}"

          />

           

          Now, because everything is bindable, the component's data will be updated automatically by Flex as soon as it is returned by the server.

          You should not have to worry about the component being created or not-Flex will handle this for you and update the data in your component.

           

          HTH

          • 2. Re: Component instantiation before xml parsing complete
            jprzybylowsk Level 1

            I've got it set up as you describe. When my component tries to instantiate it calls a function that consumes the model that's created from the xml data. The binding works but that function blows up if the model == null. I've got a hack in place that uses a recursive function to check for the model's existence before it continues but that seems very very sketchy. I think I'm on the right track though with checking for the existence of the model. A binding will be involved but I've not yet figured out how. I'm a noob so my biggest concern is best practices. I want to do things the proper way so I turn to the community to avoid my own worst tendencies =)

            Many many thanks for your input, rtalton!

             

            joel

            • 3. Re: Component instantiation before xml parsing complete
              rtalton Level 4

              The problem is you are calling a function in the component before the data are available. You just can't do that because you never can be certain when the data are returned from the server.

               

              What is it you are trying to do?

              • 4. Re: Component instantiation before xml parsing complete
                jprzybylowsk Level 1

                Thanks rtalton. At the risk of sounding like a smart *** (I don't mean to), I understand the mistake I'm making. What I have yet to comprehend is the logical solution. I'll try to explain my problem as succinctly as possible.

                 

                I use httpservice to load an xml file.

                Upon loading, I use that data to create a model--collection of value objects.

                The model is passed to my custom component via a binding.

                I use the model within my custom component to populate some accordion menus when a function is called.

                 

                The issue I am having is trying to understand when to call the function that populates my menus. How can I tell when my model object is completely instantiated and it's safe to make the function call? Here's some dummy code that I hope will demonstrate the issue.

                 

                Continued thanks!
                joel

                 

                <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" preinitialize="myService.send()" xmlns:components="components.*">

                 

                    <mx:Script>

                 

                        <![CDATA[

                 

                            import com.valueObjects.MyModel;

                 

                            import mx.rpc.events.ResultEvent;

                 

                            private var _myModel:MyModel;

                 

                 

                 

                            private function myResultHandler(e:ResultEvent):void {

                 

                                _myModel = new MyModel(e.result as XML);

                 

                            }

                 

                        ]]>

                 

                    </mx:Script>

                 

                 

                 

                    <mx:HTTPService id="myService"

                 

                            url="theXML.xml"

                 

                            result="myResultHandler(event)"

                 

                            resultFormat="e4x" />

                 

                 

                 

                    <components:MyComponent id="myComponent"

                 

                            _myCompModel="{_myModel}" />

                 

                 

                 

                </mx:Application>

                 

                 

                <!--    Contents of MyComponent custom component     -->

                 

                <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:components="components.*">

                 

                    <mx:Script>

                 

                        <![CDATA[

                 

                   

                 

                    private var _myCompModel:MyModel;

                 

                   

                 

                    public function populateAccordions():void {

                 

                   

                 

                       // Use data from _myCompModel to populate accordions.

                 

                        //If called too soon, _myCompModel is null and this throws an error (won't populate).

                 

                       // Need to know when/where to call this method from my main application.

                 

                    }

                 

                   

                 

                    ]]>

                 

                    </mx:Script>

                 

                   

                 

                    <mx:Accordion id="accordOne" />

                 

                    <mx:Accordion id="accordTwo" />

                 

                </Canvas>

                • 5. Re: Component instantiation before xml parsing complete
                  Flex harUI Adobe Employee

                  One approach would be:

                   

                   

                      <components:MyComponent id="myComponent"

                   

                              myCompModel="" />

                   

                  <!--    Contents of MyComponent custom component     --> 

                  <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"

                  xmlns:components="components.*"> 

                      <mx:Script> 

                          <![CDATA[ 

                       

                      private var _myCompModel:MyModel;

                        public function get myCompModel():MyModel

                        {

                          return _myCompModel;

                        }

                        public function set _myCompModel(value:MyModel):void

                        {

                              _myCompModel = value;

                              if (value)

                                  populateAccordions();

                        } 

                       

                      public function populateAccordions():void { 

                       

                         // Use data from _myCompModel to populate accordions. 

                          //If called too soon, _myCompModel is null and this throws an error

                   

                   

                  Another approach would be to set up the model as a Singleton.

                  • 6. Re: Component instantiation before xml parsing complete
                    jprzybylowsk Level 1

                    Thanks so much! This is the solution I've adopted.

                    I was hampered by my failure to fully comprehend the "magic" of bindings. I would look at this solution and think to myself, "how does the function get called again if value == null??" My thought was to set up some kind of recursion, not knowing that the binding was doing something akin to that behind the scenes in concert with the setter. This is a welcome revelation.

                    Tremendous thanks for all the feedback.

                    joel

                    • 7. Re: Component instantiation before xml parsing complete
                      rtalton Level 4

                      Hi Alex,

                      Can you explain a little further in your example, what calls public function set _myCompModel(value:MyModel)?

                      And if there are no data, how/when will it get called again?

                      This seems similar to my approach, so I'm trying to understand any advantage your approach may have.

                      Thank you

                      • 8. Re: Component instantiation before xml parsing complete
                        jprzybylowsk Level 1

                        I've bound the model property of my custom component to the setter method in the provided code:

                         

                        <components:MyComponent id="myComponent"
                                    _myCompModel="{_myModel}" />

                         

                        Since it's a method and not just a property you can add logic to it as he has done:

                         

                        public function set _myCompModel(value:MyModel):void

                              {

                                     _myCompModel = value;

                                    if (value)

                                  populateAccordions();

                              }

                         

                         

                        It checks to see if 'value' is non-null, or true. If it is, populateAccordions menu is called. Otherwise, nothing happens until 'value' fully instantiates, at which time the bound setter method is called again automagically.

                         

                        This is how I'm comprehending it, anyway. It works great when implemented.

                         

                        joel

                        • 9. Re: Component instantiation before xml parsing complete
                          Flex harUI Adobe Employee

                          Yup, that's the correct interpretation.  Binding is all about "change

                          notifications".  Regular variables (var) do not do any work when modified,

                          but by converting them to properties via or via get/set

                          functions, you can get them to do work like notify that they have been

                          changed, or in this case, populate a menu.

                           

                          The binding syntax {} is a shortcut for adding an event listener for the

                          appropriate change events and then evaluating the binding expression and

                          applying the new value to the left-hand-side of the "=".  As long as the

                          right-hand-side sends change events when changed, the left-hand-side will be

                          modified appropriately.

                          • 10. Re: Component instantiation before xml parsing complete
                            rtalton Level 4

                            Ok I got it.

                            Alex, Joel:

                            Thanks to both of you for helping me understand the logic. I ran into a couple of issues with the compiler complaining, but renaming the setter/getter methods fixed it (I tested with an XMLListCollection instead of a Model, getting data via HTTPService...).

                            In my implementation, like Joel, I also needed to change the UI after the data are received. This worked like a champ.

                            It is essentially overriding the getter/setter methods for the data in the custom component, allowing one to add extra tasks to the new setter method.

                             

                            I looked in the docs (Flex 3) and did find the same technique there (see: Advanced MXML Components/Adding custom properties and methods to a component/Defining properties by using setters and getters) but this capability was not obvious, at least to me.

                             

                            Another point to make here concerning use of the same data in different views using the default deferred creation policy...

                            When using this same custom component in two different views, like a Tab Navigator, the setter method will still fire after the second component instance is created (clicking on the second Tab creates it), and even though the data were already retrieved when the first Tab's children were created, the setter method will still run for the second instance of the component. Hope that helps anyone struggling with how to set up a second view based on the same data source which was already retrieved. This really comes into play when you use deep linking and users can enter the application at several points based on a bookmark, say, either Tab 1 OR Tab 2, then navigate to the other one.

                             

                            Thanks again this will make my life much easier.