Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Sharing MXML Layout Across Components

DZone's Guide to

Sharing MXML Layout Across Components

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

More Flex posts by Jeremy Mitchell can be found on blog.flexdevelopers.com


Frequently, I find myself creating visual components similar in layout and structure. Unfortunately, unlike properties and methods, layout is a tricky thing to share across components via traditional object-oriented inheritance.

Inevitably, layout code (MXML) gets duplicated across your application. The result is a maintenance nightmare and loads of redundant MXML code. Notice the redundant MXML found in the ReportEditor and ListEditor classes of this application.

Inspired by a colleague of mine, Chris Hayen, this entry will demonstrate code reuse through both inheritance (properties and methods) and composition (layout) without resorting to the undesirable use of ActionScript for layout code.

Composition over Inheritance


In this sample application, I have two editors: a report editor and a list editor. Each share a common layout, therefore, I will extract the layout into its own class - EditorLayout.

Written in MXML, EditorLayout will dictate the layout of each editor. Here's my first pass at EditorLayout.
<!--EditorLayout.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox 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="100%" height="100%">
    
    <fx:Script>
        <![CDATA[            
            [Bindable]
            public var title:String;
        ]]>
    </fx:Script>
    
    <mx:HBox width="100%"
             backgroundColor="#ededed"
             height="30"
             verticalAlign="middle"
             paddingRight="10"
             paddingLeft="10"
             borderStyle="solid"
             borderColor="#aab3b3">
        
        <mx:HBox id="toolBarChildrenContainer"/>
        
        <mx:Spacer width="100%"/>
        
        <mx:Label id="titleLabel"
                  color="#333333"
                  fontWeight="bold"
                  text="{title}"/>
    </mx:HBox>

    <mx:VBox id="editorChildrenContainer"
            width="100%"
            height="100%"
            minWidth="0"
            minHeight="0"/>

</mx:VBox>

Note: Whenever possible, I always use MXML to define layout. MXML is for layout. ActionScript is for business logic.

Although each editor will have the same layout, their contents will differ. To support custom contents, I'll add a couple of public properties to allow injection of content into the layout. I also need a couple of methods responsible for rendering this content. Here's my second pass at EditorLayout.
<!--EditorLayout.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox 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="100%" height="100%"
         initialize="onInitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            [Bindable]
            public var title:String;
            
            private var _toolBarChildren:Array;
            private var _editorChildren:Array;
            
            public function get toolBarChildren():Array
            {
                return _toolBarChildren;
            }
            
            public function set toolBarChildren(value:Array):void
            {
                _toolBarChildren = value;
            }
            
            public function get editorChildren():Array
            {
                return _editorChildren;
            }
            
            public function set editorChildren(value:Array):void
            {
                _editorChildren = value;
            }
            
            protected function onInitialize(event:FlexEvent):void
            {
                createToolBarChildren();
                createEditorChildren();
            }
            
            protected function createToolBarChildren():void
            {
                for each (var toolBarChild:DisplayObject in _toolBarChildren)
                {
                    if (!toolBarChildrenContainer.contains(toolBarChild))
                    {
                        toolBarChildrenContainer.addChild(toolBarChild);
                    }
                }
            }
            
            protected function createEditorChildren():void
            {
                for each (var editorChild:DisplayObject in _editorChildren)
                {
                    if (!editorChildrenContainer.contains(editorChild))
                    {
                        editorChildrenContainer.addChild(editorChild);
                    }
                }
            }
        ]]>
    </fx:Script>
    
    <mx:HBox width="100%"
             backgroundColor="#ededed"
             height="30"
             verticalAlign="middle"
             paddingRight="10"
             paddingLeft="10"
             borderStyle="solid"
             borderColor="#aab3b3">
        
        <mx:HBox id="toolBarChildrenContainer"/>
        
        <mx:Spacer width="100%"/>
        
        <mx:Label id="titleLabel"
                  color="#333333"
                  fontWeight="bold"
                  text="{title}"/>
    </mx:HBox>

    <mx:VBox id="editorChildrenContainer"
            width="100%"
            height="100%"
            minWidth="0"
            minHeight="0"/>

</mx:VBox>
My editors will also share some properties and methods, so I'll create a base class to leverage traditional object-oriented inheritance.
<!--Editor.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel 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:comps="comps.*"
          title="{editorType} Editor"
          width="500"
          paddingTop="10"
          paddingBottom="10"
          paddingLeft="10"
          paddingRight="10">
    
    <fx:Script>
        <![CDATA[
            public static const REPORT:String = "Report";
            public static const LIST:String = "List";
            
            [Bindable]
            public var editorType:String;

            protected function save():void
            {
                throw new Error("Abstract Class - override this method in a subclass.");
            }
        ]]>
    </fx:Script>
    
</mx:Panel>
Finally, I'll create my editor classes. An editor class will extend the Editor base class and will be composed of an instance of EditorLayout. Custom contents are injected into the shared layout declaratively using MXML.
<!--ReportEditor.mxml-->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor 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:comps="comps.*" 
              preinitialize="onPreinitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }
            
            protected function publishButton_clickHandler(event:MouseEvent):void
            {
                publish();
            }
            
            override protected function save():void
            {
                //save
            }
            
            protected function publish():void
            {
                //publish
            }

            protected function onPreinitialize(event:FlexEvent):void
            {
                editorType = Editor.REPORT;
            }

        ]]>
    </fx:Script>
    
    
    <comps:EditorLayout title="{editorType}">
        
        <comps:toolBarChildren>
            
            <s:Button id="saveButton" 
                      label="Save"
                      click="saveButton_clickHandler(event)"/>
            
            <s:Button id="publishButton" 
                      label="Publish"
                      click="publishButton_clickHandler(event)"/>
            
        </comps:toolBarChildren>
        
        <comps:editorChildren>
            
            <comps:ReportForm id="reportForm"/>
            
        </comps:editorChildren>
        
    </comps:EditorLayout>
    
</comps:Editor>

<!--ListEditor-->

<?xml version="1.0" encoding="utf-8"?>
<comps:Editor 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:comps="comps.*" 
              preinitialize="onPreinitialize(event)">
    
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            
            protected function saveButton_clickHandler(event:MouseEvent):void
            {
                save();
            }
            
            protected function submitButton_clickHandler(event:MouseEvent):void
            {
                submit();
            }
            
            override protected function save():void
            {
                //save
            }
            
            protected function submit():void
            {
                //submit
            }
            
            protected function onPreinitialize(event:FlexEvent):void
            {
                editorType = Editor.LIST;
            }
        ]]>
    </fx:Script>
    
    
    <comps:EditorLayout title="{editorType}">
        
        <comps:toolBarChildren>
            
            <s:Button id="saveButton" 
                      label="Save"
                      click="saveButton_clickHandler(event)"/>
            
            <s:Button id="submitButton" 
                      label="Submit"
                      click="submitButton_clickHandler(event)"/>
            
        </comps:toolBarChildren>
        
        <comps:editorChildren>
            
            <comps:ListForm id="listForm"/>
            
        </comps:editorChildren>
        
    </comps:EditorLayout>
    
</comps:Editor>
The final result (see below) is cleaner code throughout your application (no ActionScript layout code and no redundant MXML code).



View source is enabled

Of course, a better technique involves the use of Flex 4's skinning architecture. Stay tuned.

Update: See blog entry entitled " Flex Skinning to Share Common Layout".



Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:

Published at DZone with permission of Jeremy Mitchell, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}