apache-flexspark-skinning

Error: Skin for xxx cannot be found when adding an IVisualElement obtained from a Flex Module via a custom property


I am attempting to prototype a modular application architecture utilizing the Apache Flex SDK 4.12.1 using Flex Modules. I have the following project structure.

  1. CommonLib flex library. This project contains a single file, IPlugin.as as shown below.

    {
        import mx.core.IVisualElement;
    
        public interface IPlugIn
        {
            function get MyPlugin():IVisualElement;
        }
    }
    
  2. ModuleProject flex application. This project contains two files. PluginModule.mxml which is the module I want to load and DemoForm.mxml which is the component I want to add to the main application.

    // PluginModule.mxml
    <?xml version="1.0" encoding="utf-8"?>
    <s:Module xmlns:fx="http://ns.adobe.com/mxml/2009"
              xmlns:s="library://ns.adobe.com/flex/spark"
              xmlns:mx="library://ns.adobe.com/flex/mx"
              xmlns:local="*"
              implements="Interfaces.IPlugIn">
    
        <fx:Script>
            <![CDATA[
                import mx.core.IVisualElement;
    
                private var _myPlugin:DemoForm = new DemoForm();
    
                protected function button1_clickHandler(event:MouseEvent):void
                {
                    container.addElement(new DemoForm());
                }
    
                public function get MyPlugin():IVisualElement
                {
                    if (_myPlugin == null)
                        _myPlugin = new DemoForm();
    
                    return _myPlugin;
                }
    
            ]]>
        </fx:Script>
    
        <s:HGroup>
            <s:Button id="btn" label="Add DemoForm" click="button1_clickHandler(event)"/>
            <s:VGroup id="container"/>
        </s:HGroup>
    
    </s:Module>
    
    // Demoform.mxml
    <?xml version="1.0" encoding="utf-8"?>
    <s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" 
             xmlns:s="library://ns.adobe.com/flex/spark" 
             xmlns:mx="library://ns.adobe.com/flex/mx" width="400">
    <s:layout>
        <s:TileLayout/>
    </s:layout>
    
        <s:Button label="Button 1"/>
        <s:TextInput/>
    </s:Group>
    
  3. FlexProject flex application project. This contains a single file with the intention of loading the PluginModule and DemoForm.mxml into it's BorderContainer.

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
                   xmlns:s="library://ns.adobe.com/flex/spark" 
                   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
    
        <fx:Script>
    
            <![CDATA[
                import mx.core.IVisualElement;
                import mx.events.ModuleEvent;
                import mx.modules.IModuleInfo;
                import mx.modules.ModuleManager;
                import Interfaces.IPlugIn;
    
                private var moduleInfo:IModuleInfo;
    
                protected function button1_clickHandler(event:MouseEvent):void
                {
                    moduleInfo = ModuleManager.getModule("PluginModule.swf");
                    moduleInfo.addEventListener(ModuleEvent.READY, renderModule);
                    moduleInfo.load(null, null, null, this.moduleFactory); 
                }
    
                private function renderModule(event:ModuleEvent):void
                {
                    var module:Object = moduleInfo.factory.create();
                    var plugin:IPlugIn = module as IPlugIn;
                    container.addElement(plugin as IVisualElement); // <-- Works!
                    container.addElement(plugin.MyPlugin);          // <-- Error: Skin cannot be found
                }
            ]]>
    
        </fx:Script>
    
        <mx:VBox>
            <s:Button click="button1_clickHandler(event)" label="Add Plugin"/>
            <s:BorderContainer id="container">
                <s:layout>
                    <s:VerticalLayout gap="10"/>
                </s:layout>
            </s:BorderContainer>
        </mx:VBox>
    </s:Application>
    

I can add the module directly to the application's container and everything seems to work. The application container will then display the 1 button implemented by PluginModule.mxml. However, when I try to add the Demoform to the application's container via the plugin.MyPlugin method I receive the following error.

Error: Skin for FlexProject.ApplicationSkin2.containerGrp.contentGroup.VBox5.container.BorderContainerSkin10.Group11.DemoForm13.RadioButton16 cannot be found.

I have tried this with numerous different controls including TextArea, TextInput, and RadioButtons. These all fail with similar errors. It appears that the main application cannot find the skins for these components!

Interestingly, if I instead allow PluginModule.mxml to add the DemoForm.mxml to it's own container it works fine. Though, this is not the behavior that I desire.

Any suggestions as to what might be occurring and how I can address it?


Solution

  • A colleague found the answer. In the project which will be loading the modules you need to add -keep-all-type-selectors to the compiler options. According to the adobe docs...

    -keep-all-type-selectors

    Instructs the compiler to keep a style sheet’s type selector in a SWF file, even if that type (the class) is not used in the application. This is useful when you have a modular application that loads other applications. For example, the loading SWF file might define a type selector for a type used in the loaded (or, target) SWF file. If you set this option to true when compiling the loading SWF file, then the target SWF file will have access to that type selector when it is loaded. If you set this option to false, the compiler will not include that type selector in the loading SWF file at compile time. As a result, the styles will not be available to the target SWF file.

    This is an advanced option.