AEM Dialog Components #4: Select - Dropdown with Options
AEM Dialog Components #4: Select (Dropdown)
What is a Select?
The Select is a Granite UI component that allows authors to choose a single option from a predefined list (dropdown).
Common use cases:
- Alignment (left, center, right)
- Style variants (primary, secondary, tertiary)
- Sizes (small, medium, large)
- Content types (article, video, gallery)
- Configurations with limited options
Basic Configuration
Minimal XML Dialog
<alignment
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Text Alignment"
name="./alignment">
<items jcr:primaryType="nt:unstructured">
<left
jcr:primaryType="nt:unstructured"
text="Left"
value="left"/>
<center
jcr:primaryType="nt:unstructured"
text="Center"
value="center"/>
<right
jcr:primaryType="nt:unstructured"
text="Right"
value="right"/>
</items>
</alignment>Structure:
- Container
<select>withsling:resourceTypeandname - Child node
<items>containing the options - Each option has
text(visible label) andvalue(saved value)
Main Properties
1. Default Value
<alignment
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Text Alignment"
name="./alignment"
value="left">
<items jcr:primaryType="nt:unstructured">
<left
text="Left"
value="left"
selected="{Boolean}true"/>
<center
text="Center"
value="center"/>
<right
text="Right"
value="right"/>
</items>
</alignment>Two ways:
value="left"on the containerselected="{Boolean}true"on the option
2. Required
<componentType
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Component Type"
name="./componentType"
required="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<!-- options -->
</items>
</componentType>3. Empty Option
<theme
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Theme"
name="./theme"
emptyText="-- Select Theme --">
<items jcr:primaryType="nt:unstructured">
<light
text="Light"
value="light"/>
<dark
text="Dark"
value="dark"/>
</items>
</theme>emptyText adds an empty option at the top of the list.
Practical Examples
Example 1: Text Alignment
<alignment
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Text Alignment"
name="./alignment"
value="left">
<items jcr:primaryType="nt:unstructured">
<left
jcr:primaryType="nt:unstructured"
text="Left"
value="left"/>
<center
jcr:primaryType="nt:unstructured"
text="Center"
value="center"/>
<right
jcr:primaryType="nt:unstructured"
text="Right"
value="right"/>
<justify
jcr:primaryType="nt:unstructured"
text="Justify"
value="justify"/>
</items>
</alignment>HTL:
<div class="content text-${properties.alignment || 'left'}">
${properties.text}
</div>Example 2: Style Variants
<variant
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Button Variant"
name="./variant"
value="primary">
<items jcr:primaryType="nt:unstructured">
<primary
jcr:primaryType="nt:unstructured"
text="Primary (Blue)"
value="primary"/>
<secondary
jcr:primaryType="nt:unstructured"
text="Secondary (Gray)"
value="secondary"/>
<success
jcr:primaryType="nt:unstructured"
text="Success (Green)"
value="success"/>
<danger
jcr:primaryType="nt:unstructured"
text="Danger (Red)"
value="danger"/>
</items>
</variant>HTL:
<button class="btn btn-${properties.variant || 'primary'}">
${properties.buttonText}
</button>Example 3: Sizes
<size
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Component Size"
name="./size"
value="medium">
<items jcr:primaryType="nt:unstructured">
<small
jcr:primaryType="nt:unstructured"
text="Small"
value="sm"/>
<medium
jcr:primaryType="nt:unstructured"
text="Medium"
value="md"
selected="{Boolean}true"/>
<large
jcr:primaryType="nt:unstructured"
text="Large"
value="lg"/>
<xlarge
jcr:primaryType="nt:unstructured"
text="Extra Large"
value="xl"/>
</items>
</size>HTL:
<div class="component component-${properties.size || 'md'}">
Content
</div>Example 4: Content Type
<contentType
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Content Type"
fieldDescription="Select the type of content to display"
name="./contentType"
required="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<article
jcr:primaryType="nt:unstructured"
text="Article"
value="article"/>
<video
jcr:primaryType="nt:unstructured"
text="Video"
value="video"/>
<gallery
jcr:primaryType="nt:unstructured"
text="Image Gallery"
value="gallery"/>
<infographic
jcr:primaryType="nt:unstructured"
text="Infographic"
value="infographic"/>
</items>
</contentType>Usage: Show different templates/layouts based on type.
Complete Dialog with Select
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:granite="http://www.adobe.com/jcr/granite/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="Hero Banner"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<tabs
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/tabs">
<items jcr:primaryType="nt:unstructured">
<!-- Content Tab -->
<content
jcr:primaryType="nt:unstructured"
jcr:title="Content"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<!-- Title -->
<title
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Title"
name="./title"
required="{Boolean}true"/>
<!-- Subtitle -->
<subtitle
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Subtitle"
name="./subtitle"/>
</items>
</content>
<!-- Style Tab -->
<style
jcr:primaryType="nt:unstructured"
jcr:title="Style"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<!-- Text Alignment -->
<alignment
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Text Alignment"
name="./alignment"
value="center">
<items jcr:primaryType="nt:unstructured">
<left
jcr:primaryType="nt:unstructured"
text="Left"
value="left"/>
<center
jcr:primaryType="nt:unstructured"
text="Center"
value="center"
selected="{Boolean}true"/>
<right
jcr:primaryType="nt:unstructured"
text="Right"
value="right"/>
</items>
</alignment>
<!-- Theme -->
<theme
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Theme"
name="./theme"
value="light">
<items jcr:primaryType="nt:unstructured">
<light
jcr:primaryType="nt:unstructured"
text="Light"
value="light"/>
<dark
jcr:primaryType="nt:unstructured"
text="Dark"
value="dark"/>
</items>
</theme>
<!-- Size -->
<size
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Height"
name="./size"
value="medium">
<items jcr:primaryType="nt:unstructured">
<small
jcr:primaryType="nt:unstructured"
text="Small (400px)"
value="small"/>
<medium
jcr:primaryType="nt:unstructured"
text="Medium (600px)"
value="medium"/>
<large
jcr:primaryType="nt:unstructured"
text="Large (800px)"
value="large"/>
<fullscreen
jcr:primaryType="nt:unstructured"
text="Full Screen"
value="fullscreen"/>
</items>
</size>
</items>
</style>
</items>
</tabs>
</items>
</content>
</jcr:root>Usage in HTL
Simple Value Reading
<div class="hero
hero-${properties.size || 'medium'}
hero-${properties.theme || 'light'}
text-${properties.alignment || 'center'}">
<h1>${properties.title}</h1>
<p>${properties.subtitle}</p>
</div>With Fallback
<!-- Fallback with || operator -->
<div class="component-${properties.variant || 'default'}">
Content
</div>
<!-- Fallback with data-sly-test -->
<div class="component">
<sly data-sly-test="${properties.theme == 'dark'}">
<!-- Dark theme content -->
</sly>
<sly data-sly-test="${!properties.theme || properties.theme == 'light'}">
<!-- Light theme content (default) -->
</sly>
</div>With Sling Model
@Model(adaptables = Resource.class)
public class HeroModel {
@ValueMapValue
private String title;
@ValueMapValue
private String subtitle;
@ValueMapValue
private String alignment;
@ValueMapValue
private String theme;
@ValueMapValue
private String size;
public String getTitle() {
return title;
}
public String getSubtitle() {
return subtitle;
}
public String getAlignment() {
return alignment != null ? alignment : "center";
}
public String getTheme() {
return theme != null ? theme : "light";
}
public String getSize() {
return size != null ? size : "medium";
}
/**
* Complete CSS classes
*/
public String getHeroClasses() {
return String.format("hero hero-%s hero-%s text-%s",
getSize(), getTheme(), getAlignment());
}
/**
* Check if dark theme
*/
public boolean isDarkTheme() {
return "dark".equals(getTheme());
}
}HTL with Model:
<div data-sly-use.model="com.mysite.models.HeroModel"
class="${model.heroClasses}">
<div class="hero-content">
<h1>${model.title}</h1>
<p data-sly-test="${model.subtitle}">
${model.subtitle}
</p>
</div>
</div>Dynamic Options from Datasource
Select Configuration with Datasource
<categorySelect
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/select"
fieldLabel="Category"
name="./category"
datasource="com.mysite.datasources.CategoryDataSource">
</categorySelect>Java Datasource
package com.mysite.datasources;
import com.adobe.granite.ui.components.ds.DataSource;
import com.adobe.granite.ui.components.ds.SimpleDataSource;
import com.adobe.granite.ui.components.ds.ValueMapResource;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.osgi.service.component.annotations.Component;
import javax.servlet.Servlet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Component(
service = Servlet.class,
property = {
"sling.servlet.resourceTypes=com.mysite.datasources.CategoryDataSource"
}
)
public class CategoryDataSource extends SlingSafeMethodsServlet {
@Override
protected void doGet(SlingHttpServletRequest request,
SlingHttpServletResponse response) {
ResourceResolver resolver = request.getResourceResolver();
List<Resource> resources = new ArrayList<>();
// Fetch categories from your system
List<Category> categories = getCategoriesFromService();
for (Category cat : categories) {
ValueMap vm = new ValueMapDecorator(new HashMap<>());
vm.put("value", cat.getId());
vm.put("text", cat.getTitle());
resources.add(new ValueMapResource(resolver,
new ResourceMetadata(),
"nt:unstructured",
vm));
}
DataSource ds = new SimpleDataSource(resources.iterator());
request.setAttribute(DataSource.class.getName(), ds);
}
private List<Category> getCategoriesFromService() {
// Implementation to fetch categories
return new ArrayList<>();
}
}Select vs Radio vs Checkbox
| Component | When to Use | Values |
|---|---|---|
| Select | 3+ options, limited space | 1 value from N |
| Radio | 2-5 options, visibility important | 1 value from N |
| Checkbox | Enable/disable single option | Boolean |
| Checkbox Group | Multiple selection | Array of values |
Use Select when: You have many options and want to save space in the dialog.
Common Issues
❌ Problem 1: Value not saved
<!-- WRONG - missing value in option -->
<items jcr:primaryType="nt:unstructured">
<left
text="Left"/>
</items>
<!-- CORRECT -->
<items jcr:primaryType="nt:unstructured">
<left
text="Left"
value="left"/>
</items>❌ Problem 2: Default not working
<!-- WRONG - value as string and selected together -->
<alignment
value="center">
<items jcr:primaryType="nt:unstructured">
<left
selected="true"/>
</items>
</alignment>
<!-- CORRECT - use one or the other with type hint -->
<alignment
value="center">
<!-- OR -->
<items jcr:primaryType="nt:unstructured">
<left
selected="{Boolean}true"/>
</items>
</alignment>❌ Problem 3: Duplicate options
<!-- WRONG - node names must be unique -->
<items jcr:primaryType="nt:unstructured">
<option
text="Option 1"
value="opt1"/>
<option
text="Option 2"
value="opt2"/>
</items>
<!-- CORRECT - different node names -->
<items jcr:primaryType="nt:unstructured">
<option1
text="Option 1"
value="opt1"/>
<option2
text="Option 2"
value="opt2"/>
</items>Best Practices
- ✅ Clear values:
value="left"notvalue="l"orvalue="0" - ✅ Descriptive text: "Left Align" not just "Left"
- ✅ Sensible default: Always set a default value
- ✅ Unique node names:
<left>,<center>,<right>not<option> - ✅ Fallback in HTL: Use
||operator for default values - ✅ fieldDescription: Explain the impact of the choice
- ✅ Logical order: Put the most common options at the top
- ✅ Datasource for dynamic lists: Don't hard-code long lists
Next Lesson
In the next lesson we'll cover the PathBrowser component for selecting AEM content.
Resources:
Guide #4 of the AEM Dialog Components series - ← Previous lesson | Next lesson →