Override a component dialog by using Sling Resource Merger

Starting from AEM 6, there’s a cool functionality called Sling Resource Merger. It allows us to override a component dialog of some parent (super) component with a less XML code.

Let’s demonstrate it on some example. Suppose that we have a component with the following dialog definition:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Parent Component"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/tabs"
type="nav"/>
<items jcr:primaryType="nt:unstructured">
<generalTab
jcr:primaryType="nt:unstructured"
jcr:title="General"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<columns
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Title"
name="./title"/>
<subtitle
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Subtitle"
name="./subtitle"/>
<titleFilter
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Title Filter"
name="./titleFilter"/>
<showSeriesFilter
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/checkbox"
name="./showSeriesFilter"
text="Show Series Filter"
value="true"/>
<seriesFilterTitle
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Series Filter Title"
name="./seriesFilterTitle"/>
</items>
</columns>
</items>
</generalTab>
<advancedTab
jcr:primaryType="nt:unstructured"
jcr:title="Advanced"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<columns
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<advancedConf1
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Advanced Config 1"
name="./advancedConf1"/>
<advancedConf2
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
fieldLabel="Advanced Config 2"
name="./advancedConf2"/>
</items>
</columns>
</items>
</advancedTab>
</items>
</content>
</jcr:root>

The component dialog has two tabs with four and two fields, respectively. Now let’s define a new component by extending the existing one. All fields from General tab should appear in a dialog of the subcomponent but two of them, showSeriesFilter and seriesFilterTitle. Also, Advanced tab is not needed for the subcomponent.
The dialog definition should look like this one:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Subcomponent"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/tabs"
type="nav"/>
<items jcr:primaryType="nt:unstructured"
sling:hideChildren="[advancedTab]">
<generalTab
jcr:primaryType="nt:unstructured"
jcr:title="General"
sling:resourceType="granite/ui/components/foundation/container">
<layout
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"/>
<items jcr:primaryType="nt:unstructured">
<columns
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/container">
<items jcr:primaryType="nt:unstructured">
<showSeriesFilter
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/checkbox"
sling:hideResource="{Boolean}true"/>
<seriesFilterTitle
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/textfield"
sling:hideResource="{Boolean}true"/>
</items>
</columns>
</items>
</generalTab>
</items>
</content>
</jcr:root>

Because there’s the inheritance relationship between these two components (defined by sling:resourceSuperType property), Sling Resource Merger will merge the two dialog definitions. sling:hideResource property is the instruction for the merger to hide the corresponding property in a result of the dialog definition merge, and sling:hideChildren property causes that the merger will hide the inherited Advanced tab. All other fields will be copied during the merge.

Before the merger has been introduced, a developer had to do the following:

  • create the subcomponent dialog definition by copying/pasting the parent component dialog definition
  • do the necessary modifications (for this sample, remove all unwanted fields from General tab and remove Advanced tab definition)

You can find the detailed documentation about Sling Resource Merger here.