apache / royale-asjs

Apache Royale ASJS
Apache License 2.0
372 stars 116 forks source link

Uncaught TypeError: Cannot set property 'html' of undefined on j:Label on SDK 0.9.6 #499

Open cristallium opened 5 years ago

cristallium commented 5 years ago

Hi,

After several hours to try differents tests cases the following code thrown an Cannot set property 'html' of undefined on a j:Label and I don't understand why.

MonComptePanel.mxml

<j:ScrollableSectionContent 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:j="library://ns.apache.org/royale/jewel" 
    xmlns:js="library://ns.apache.org/royale/basic" 
    initComplete="ev_initComplete()">

    <js:states>
        <js:State name="login"/>
        <js:State name="loggued"/>
    </js:states>

    <j:beads>
        <js:SimpleStatesImpl/>
    </j:beads>

    <j:Group includeIn="login">
        <j:Label id="lbl_ident"/>
    </j:Group>

    <fx:Script>
        <![CDATA[

            private function ev_initComplete():void {
                this.currentState = "login"; // this is useless
                var str:String = "<p style='color:#ff6600'>Please log in.</p>";
                lbl_ident.html = str; // here is the issue
            }

        ]]>
    </fx:Script>

</j:ScrollableSectionContent>

To reproduce the issue, code must be in Main.mxlx

<j:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
                   xmlns:js="library://ns.apache.org/royale/basic"  
                   xmlns:j="library://ns.apache.org/royale/jewel" 
                   xmlns:binding="org.apache.royale.binding.*" 
                   xmlns:local="*">
    <j:valuesImpl>
        <js:SimpleCSSValuesImpl />
    </j:valuesImpl>

    <j:initialView >
        <j:View>
            <j:ApplicationMainContent selectedContent = "moncompte_panel">
                <local:MonComptePanel name="moncompte_panel"/>
            </j:ApplicationMainContent>
        </j:View>
    </j:initialView>
</j:Application>

The full error is

EventDispatcher.js:73 Uncaught TypeError: Cannot set property 'html' of undefined
    at MonComptePanel.MonComptePanel_ev_initComplete (MonComptePanel.mxml:28)
    at MonComptePanel.$EH0 (MonComptePanel.mxml:7)
    at MonComptePanel.goog.events.EventTarget.fireListeners (eventtarget.js:284)
    at Function.goog.events.EventTarget.dispatchEventInternal_ (eventtarget.js:382)
    at MonComptePanel.goog.events.EventTarget.dispatchEvent (eventtarget.js:196)
    at MonComptePanel.org.apache.royale.events.EventDispatcher.dispatchEvent (EventDispatcher.js:70)
    at MonComptePanel.org.apache.royale.core.HTMLElementWrapper.dispatchEvent (HTMLElementWrapper.js:254)
    at MonComptePanel.org.apache.royale.jewel.Container.addedToParent (Container.js:180)
    at org.apache.royale.jewel.ApplicationMainContent.org.apache.royale.core.UIBase.addElement (UIBase.js:410)
    at org.apache.royale.jewel.ApplicationMainContent.org.apache.royale.core.GroupBase.addElement (GroupBase.js:165)

Here is full source code

TestRoyale4.zip

Any ideas ? Regards

carlosrovira commented 5 years ago

I think that's interesting and hope @aharui could give us a hand here.

The problem seems to be caused by states and includeIn. So referencing components in views where we have states, or even using bindings could make Royale not able to reference the component if we use includeIn. IMHO, this is a bug in states, since it fails in this kind of situations.

I think a workaround for this is use visibility instead of includeIn. Check this for an example in both formats: https://royale.apache.org/using-view-states-to-show-or-hide-content/

cristallium commented 5 years ago

Thanks for your quick interest and workaround

For now I do a lot of tries and errors and it seems that the smallest code to reproduce it, is the given exemple. If trying directly on the first view like below it is working

<j:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
                   xmlns:js="library://ns.apache.org/royale/basic"  
                   xmlns:j="library://ns.apache.org/royale/jewel" 
                   xmlns:binding="org.apache.royale.binding.*" 
                   xmlns:local="*" applicationComplete="ev_applicationComplete()">
    <j:valuesImpl>
        <js:SimpleCSSValuesImpl />
    </j:valuesImpl>

    <j:initialView >

        <j:View id="myview">
            <js:states>
                <js:State name="login"/>
                <js:State name="loggued"/>
            </js:states>

            <j:beads>
                <js:SimpleStatesImpl/>
            </j:beads>

            <j:Group includeIn="login">
                <j:Label id="lbl_ident"/>
            </j:Group>

        </j:View>
    </j:initialView>

    <fx:Script>
        <![CDATA[
            private function ev_applicationComplete():void {
                lbl_ident.html = "HELLO";
            }
        ]]>
    </fx:Script>
</j:Application>

I hope it will be easy to fix it

aharui commented 5 years ago

Make sure there is an IStatesImpl in the CSS for the application. I'm not sure if Jewel has one by default. Don't look at the app's .css output because IStatesImpl will be pruned, try text searching the output .js file for "StatesImpl".

carlosrovira commented 5 years ago

@aharui Jewel needs to add the bead (same as Basic). Fred did it in the first example, but not in the latest:

<j:beads>
        <js:SimpleStatesImpl/>
    </j:beads>

We have in Tour de Jewel an example of use for both includeIn and visible alternatives.

The problem Fred was experimenting is about Royale getting undefined values for instances when using includeIn, and I was saying that I think is a bug since I found it while developing our real Royale App.

Sometimes when we have binding or other things in place and use view states with includeIn Royale does not have the "hidden" components instantiated and this results in a javascript error reporting undefined. I think Royale should know about the object to avoid code runtime errors.

aharui commented 5 years ago

This is a timing problem. When added as a bead, the IStatesImpl relies on the initComplete to initialize, but if the application is also relying on a state change in initComplete, the application's handler will get called before the IStatesImpl gets initialized.

The Containers are set up to initialize their IStatesImpls when the states property is assigned its states. That gets them in early to be the first to get the initComplete event. So, if folks are relying on state changes in initComplete, they should use CSS instead of beads to setup the IStatesImpl. The MXRoyale and SparkRoyale defaults.css setup a default IStatesImpl for all containers. Jewel could do the same.

This version uses CSS instead of beads and seems to work.

<j:ScrollableSectionContent 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:j="library://ns.apache.org/royale/jewel" 
    xmlns:js="library://ns.apache.org/royale/basic" 
    initComplete="ev_initComplete()">

    <js:states>
        <js:State name="login"/>
        <js:State name="loggued"/>
    </js:states>

    <j:Group includeIn="login">
        <j:Label id="lbl_ident"/>
    </j:Group>

    <fx:Style>
        global {
            IStatesImpl:            ClassReference("org.apache.royale.core.SimpleStatesImpl");
        }
    </fx:Style>
    <fx:Script>
        <![CDATA[

            private function ev_initComplete():void {
                this.currentState = "login";
                var str:String = "<p style='color:#ff6600'>Veuillez vous identifier.</p>";
                lbl_ident.html = str;
            }

        ]]>
    </fx:Script>

</j:ScrollableSectionContent>
carlosrovira commented 5 years ago

Hi Alex, thanks for exposing how to solve the problem. I think this will be something many people will need to know.

Maybe this could be solved at framework level so users don't need to use this workaround and both ways will reach to the same working solution? (CSS bead and adding the bead in MXML)

Thanks