Day 16 of 40
Sling Models In Depth
Injectors, adaptables, the delegation pattern, and Model Exporter
By the end of today you should be able to
- Write a Sling Model with the right adaptables and injection annotations
- Choose correctly between @ValueMapValue, @ChildResource, @SlingObject and @OSGiService
- Explain the delegation pattern for extending a Core Component's model
- Expose a model as JSON with Sling Model Exporter
What a Sling Model is
A Sling Model is a plain Java class, annotated so that Sling can
adapt a Resource or SlingHttpServletRequest into it and inject
values automatically. It is the "controller" of an AEM component: it reads the repository, does the
thinking, and exposes clean getters that HTL renders.
@Model(
adaptables = { SlingHttpServletRequest.class },
adapters = { Byline.class },
resourceType = "wknd/components/byline",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class BylineImpl implements Byline {
@ValueMapValue
private String name;
@ChildResource
private List<Resource> occupations;
@SlingObject
private ResourceResolver resourceResolver;
@OSGiService
private QueryBuilder queryBuilder;
@PostConstruct
private void init() { /* runs after injection */ }
@Override
public String getName() { return name; }
}
The annotations that matter
@ValueMapValue— inject a property from the resource. The field name is the property name; use@Namedwhen they differ (as forjcr:title).@ChildResource— inject a child resource or a list of them. This is how multifields are read.@SlingObject— inject a Sling context object:ResourceResolver,Resource,SlingHttpServletRequest.@OSGiService— inject an OSGi service.@Self— inject the adaptable itself, commonly to adapt it to something else.@ScriptVariable— inject an HTL binding such ascurrentPageorpageManager.@PostConstruct— run initialisation after all injection completes.
The default is REQUIRED, which means a single missing property makes the whole adaptation return null — and your component silently renders nothing. OPTIONAL leaves absent fields null and lets the model still build. The archetype sets this for you; keep it.
Resource vs Request adaptable
- Adapt from
Resourcewhen the model needs only repository data. Simpler, and usable outside a request. - Adapt from
SlingHttpServletRequestwhen you need request context — the current page, selectors, the Style System's applied CSS classes, or anything from the request bindings. Core Components adapt from the request.
Declaring resourceType registers the model for that resource type, so HTL can use data-sly-use with the interface and Sling picks the right implementation.
The delegation pattern
Extending a Core Component's model is a specific problem: you want to add one method while keeping all of Adobe's behaviour. Reimplementing the interface would mean writing every method. The delegation pattern solves it:
@Model(adaptables = SlingHttpServletRequest.class,
adapters = { Teaser.class, ComponentExporter.class },
resourceType = "wknd/components/teaser")
public class TeaserImpl implements Teaser {
@Self @Via(type = ResourceSuperType.class)
private Teaser delegate; // Adobe's implementation
@ValueMapValue
private String customField; // your addition
@Override
public String getTitle() { return delegate.getTitle(); } // pass through
public String getCustomField() { return customField; } // yours
}
@Via(type = ResourceSuperType.class) is the key: it adapts using the
supertype's resource type, giving you Adobe's model to delegate to. Lombok's
@Delegate can generate the pass-through methods so you only write what you change.
Sling Model Exporter
Adding two annotations turns a model into a JSON endpoint:
@Model(adaptables = SlingHttpServletRequest.class,
adapters = { Byline.class, ComponentExporter.class },
resourceType = "wknd/components/byline")
@Exporter(name = "jackson", extensions = "json")
public class BylineImpl implements Byline { ... }
The component's content is then available at
/content/.../byline.model.json. This is what powers the SPA Editor and gives you a
cheap API for a widget without writing a servlet. Control the output with Jackson annotations —
@JsonProperty, @JsonIgnore.
If a model is exported, its JSON is reachable at that URL by anyone who can reach the page. Do not expose internal fields, and make sure Dispatcher filters allow .model.json only where you intend.
Models hold the logic. Choose the adaptable deliberately, keep injection OPTIONAL, and use @Via(ResourceSuperType) to extend Core Components instead of reimplementing them.
Watch
Adobe's own videos for this topic. They load only when you press play.
Read on Experience League
The primary sources these notes are drawn from.
Your notes
Saved automatically to this browser.
Check yourself
11 questions on today's material. 80% to pass.