Nested child nodes within custom MXML components in Flex

I wanted to share this since I’ve lost too much time to find the right information and I hope this can be helpful.

Perhaps you are building your own MXML components in Flex and you arrived at the point where you want to create kind of “templates” that you can feed with different content, aka other MXML components. If so, you’ll probably stuck in an error like:

Error: Multiple sets of visual children have been specified for this component (component definition and component instance).
at mx.core::Container/initialize()

Furthermore, you’d like to have control over the child nodes in order to insert them into your own defined “nesting point”. Let’s say a Panel called container in your custom MXML component. If so, you’ve probably tried to override the addChild method without luck and keeping the error above.

That’s the best solution I could find so far:

1) In your custom component, create a setter function where to collect the child nodes received within your MXML component tag. Store them temporarily into an Array:

private var _components:Array;

public function set contentChild(value:Array):void{
_components = value;
}

2) Create a function to call once your component is completely created and where you actually add the collected child nodes into your component:

/*
 * in this case i want them to go inside a panel called container
 */
private function addComponents():void{
	container.removeAllChildren();

	for(var i:int=0; i < _components.length; i++){
		container.addChild( _components[i] );
	}
}

3) In your custom component you must define the setter property as the default in the metadata section of your MXML component. Full MXML example:

<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute"
	verticalAlign="top"
	width="100%"
	height="100%"
	pageTitle=""
	horizontalScrollPolicy="off"
	verticalScrollPolicy="off"
	creationComplete="addComponents()"> <!-- tell to add childs once created -->

	<mx:Script>
		<![CDATA[
			private var _components:Array;

			public function set contentChild(value:Array):void{
				_components = value;
			}

			private function addComponents():void{
				container.removeAllChildren();

				for(var i:int=0; i < _components.length; i++){
					container.addChild( _components[i] );
				}
			}
		]]>
	</mx:Script>

	<!-- define default property so child nodes know where to go -->
	<mx:Metadata>
		[DefaultProperty("contentChild")]
	</mx:Metadata>

	<mx:Panel id="base" width="100%" height="100%" styleName="interfacePanel" layout="vertical">
		<mx:Panel id="container" width="100%" height="100%" styleName="interfaceInnerPanel" layout="absolute">
			<!-- child nodes will come here! -->
		</mx:Panel>
	</mx:Panel>
</mx:Module>

4) Now you can nest components within your custom component:

<custom:InterfacePanel id="savePanel" >
		<mx:Label y="20" styleName="headline" text="Enter your name" />
		<mx:TextInput id="fName" y="50" width="250" />
		<mx:Label y="100" styleName="headline" text="Enter your birthdate" />
		<mx:DateField id="fDate" y="130"  width="250" formatString="DD/MM/YYYY" firstDayOfWeek="1" yearNavigationEnabled="true"/>
		<mx:Button id="save" y="180" label="Save"/>
</custom:InterfacePanel>

I hope this could be helpful!
Source: http://weblogs.macromedia.com/pent/archives/2006/03/component_templ.html

Have your say:




I merely need to tell you that you have written an superb and special article that I genuinely enjoyed reading. I am fascinated by how nicely you laid out your material and presented your views. Thank you.

[...] have found this script-based method, which might work, but I would like a cleaner solution with MXML only. How do I alter the code [...]

jocabola says:

Thanks for sharing this Brent!
Love to see this post is helping some people…

I had some issues when I tried to add a mx:Form component to this implementation. I resolved them by doing something like this:

http://tinyurl.com/ykq669p

It works great! Thanks.

Lacy Garrison says:

Amazing! Thanks you.

Andre says:

Thanks, this was just what i needed ;-) )

Karl says:

exactly what i was looking for and really clearly explained. nice to actually learn something!!!!
thanks for doin that :D

nice! i’m gonna make my own journal

Thanks man! Helped me a lot… Just what i was looking for.

jocabola says:

Hi Richard,

never tried to use this further on a subclass or child of the main component.
Notice that this is just a workaround, the best solution I could find searching the net.

I have been using this without problems always facing it the same way:

1) Create a “complex” component with a desired nesting point for the child MX components

2) Use it as a regular component, being able to nest MX components such as buttons, text fields, etc. on the desired nesting point

Notice that the nested elements will be layouted according to your nesting element definitions (vertical, horizontal, absolute, …)

Once again, never tried to add a second nesting level, although I guess it should work as well.

Cheers,

I cannot get this to work beyond the first generation: a sub-class of this component cannot incrementally add children, even if I duplicate the DefaultProperty protocol.

I’m coming from Delphi’s visual component inheritance, and this seems a bit rough.

Cheers

jocabola says:

Hi Jesse,
I am using Flex 3.2.0 and works fine. Also worked in previous build 3.1.X.

Good luck!

Jesse says:

Does this work in Flex 3? I get an error:

Error: Multiple sets of visual children have been specified for this component (base component definition and derived component definition).
at mx.core::Container/http://www.adobe.com/2006/flex/mx/internal::setDocumentDescriptor()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\Container.as:3421]

RYErnest says:

Nice post u have here :D Added to my RSS reader


Welcome to the web archive of Eduard Prats Molner, a developer exploring interactive media, user experience and visualization.