AEM Dialog Components #5: PathBrowser - Selezione Contenuti AEM
AEM Dialog Components #5: PathBrowser
Cos'è un PathBrowser?
Il PathBrowser (o PathField) è un componente Granite UI che permette agli autori di selezionare contenuti dal repository AEM tramite un'interfaccia di navigazione.
Casi d'uso comuni:
- Selezione pagine interne
- Link a contenuti
- Selezione immagini da DAM
- Reference ad assets
- Selezione Experience Fragments
Configurazione Base
XML Dialog Minimo
<pagePath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Page"
name="./pagePath"
rootPath="/content"/>Proprietà essenziali:
sling:resourceType- Sempregranite/ui/components/coral/foundation/form/pathfieldfieldLabel- Etichettaname- Percorso JCR (usa sempre./)rootPath- Percorso radice della navigazione
Proprietà Principali
1. rootPath (Percorso Radice)
<!-- Pagine sotto /content -->
<pagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Internal Page"
name="./pagePath"
rootPath="/content/mysite"/>
<!-- Assets dal DAM -->
<imagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
name="./imagePath"
rootPath="/content/dam"/>Best practice: Limita sempre a un sottopercorso specifico!
2. filter (Filtro Tipo Risorsa)
<!-- Solo pagine -->
<pagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Page Link"
name="./pagePath"
rootPath="/content/mysite"
filter="hierarchyNotFile"/>
<!-- Solo immagini -->
<imagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
name="./imagePath"
rootPath="/content/dam"
filter="image"/>Filtri comuni:
hierarchyNotFile- Solo pagine (no assets)image- Solo immaginivideo- Solo videofile- Solo file- Custom predicates
3. Required
<imagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Hero Image"
name="./imagePath"
rootPath="/content/dam/mysite"
filter="image"
required="{Boolean}true"/>4. forceSelection (Forza Selezione via Browser)
<pagePath
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Page"
name="./pagePath"
rootPath="/content"
forceSelection="{Boolean}true"/>Comportamento: Disabilita input manuale, obbliga a usare il browser.
Esempi Pratici
Esempio 1: Link Interno a Pagina
<internalLink
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Internal Link"
fieldDescription="Select a page from your site"
name="./internalLink"
rootPath="/content/mysite"
filter="hierarchyNotFile"/>HTL:
<a href="${properties.internalLink}.html">
${properties.linkText}
</a>Esempio 2: Immagine da DAM
<fileReference
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
fieldDescription="Select an image from DAM"
name="./fileReference"
rootPath="/content/dam/mysite"
filter="image"
required="{Boolean}true"/>HTL:
<img src="${properties.fileReference}" alt="${properties.altText}"/>Esempio 3: Experience Fragment
<experienceFragment
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Experience Fragment"
fieldDescription="Select a header experience fragment"
name="./experienceFragment"
rootPath="/content/experience-fragments/mysite"
filter="folder,cq:Page"/>Esempio 4: Video da DAM
<videoPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Video"
name="./videoPath"
rootPath="/content/dam/mysite/videos"
filter="video"/>HTL:
<video controls>
<source src="${properties.videoPath}" type="video/mp4"/>
</video>Esempio 5: PDF o Documento
<documentPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Download Document"
fieldDescription="Select a PDF or document"
name="./documentPath"
rootPath="/content/dam/mysite/documents"
filter="file"/>HTL:
<a href="${properties.documentPath}" download>
Download ${properties.documentTitle}
</a>Dialog Completa con PathBrowser
<?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="Content Card"
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"/>
<!-- Image from DAM -->
<fileReference
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Image"
fieldDescription="Select card image"
name="./fileReference"
rootPath="/content/dam/mysite"
filter="image"
required="{Boolean}true"/>
<!-- Alt Text -->
<altText
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Alt Text"
name="./altText"
required="{Boolean}true"/>
<!-- Link to Page -->
<linkPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Link"
fieldDescription="Link to internal page"
name="./linkPath"
rootPath="/content/mysite"
filter="hierarchyNotFile"/>
</items>
</content>
</items>
</tabs>
</items>
</content>
</jcr:root>Uso in HTL
Pagina Interna
<a data-sly-test="${properties.linkPath}"
href="${properties.linkPath}.html">
${properties.linkText}
</a>Immagine da DAM
<img data-sly-test="${properties.fileReference}"
src="${properties.fileReference}"
alt="${properties.altText || properties.title}"/>Con Sling Model
@Model(adaptables = Resource.class)
public class ContentCardModel {
@ValueMapValue
private String title;
@ValueMapValue
private String fileReference;
@ValueMapValue
private String altText;
@ValueMapValue
private String linkPath;
public String getTitle() {
return title;
}
public String getImagePath() {
return fileReference;
}
public String getAltText() {
return altText != null ? altText : title;
}
public String getLinkUrl() {
return linkPath != null ? linkPath + ".html" : null;
}
public boolean hasImage() {
return fileReference != null && !fileReference.isEmpty();
}
public boolean hasLink() {
return linkPath != null && !linkPath.isEmpty();
}
}HTL con Model:
<div data-sly-use.model="com.mysite.models.ContentCardModel" class="card">
<!-- Image -->
<img data-sly-test="${model.hasImage}"
src="${model.imagePath}"
alt="${model.altText}"
class="card-image"/>
<!-- Title -->
<h3 class="card-title">${model.title}</h3>
<!-- Link -->
<a data-sly-test="${model.hasLink}"
href="${model.linkUrl}"
class="card-link">
Read More
</a>
</div>Filtri Avanzati
Filter Personalizzato con Predicate
<customPathField
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Custom Filter"
name="./customPath"
rootPath="/content"
filter="myCustomFilter"
optionLoader="com.mysite.predicates.CustomPathFieldPredicateLoader"/>Java Predicate Loader
@Component(service = PredicateLoader.class)
public class CustomPathFieldPredicateLoader implements PredicateLoader {
@Override
public Predicate load(Request request) {
return new Predicate() {
@Override
public boolean includes(Resource resource) {
// Custom logic per filtrare risorse
return resource.getPath().contains("specific-folder");
}
};
}
}PathField vs PathBrowser
Sono sinonimi - stesso componente, nomi diversi:
granite/ui/components/coral/foundation/form/pathfieldgranite/ui/components/coral/foundation/form/pathbrowser
Usa pathfield (più comune nella documentazione).
Problemi Comuni
❌ Problema 1: rootPath troppo ampio
<!-- SCONSIGLIATO - permette navigazione ovunque -->
<pagePath
rootPath="/"/>
<!-- MEGLIO - limita alla tua area -->
<pagePath
rootPath="/content/mysite"/>Perché: Troppa libertà confonde gli autori e permette selezioni sbagliate.
❌ Problema 2: Manca .html nell'HTL
<!-- SBAGLIATO - path senza estensione -->
<a href="${properties.linkPath}">Link</a>
<!-- CORRETTO - aggiungi .html -->
<a href="${properties.linkPath}.html">Link</a>
<!-- OPPURE usa Link Externalizer nel Model -->❌ Problema 3: Image path senza validazione
<!-- RISCHIOSO - se path è vuoto -->
<img src="${properties.fileReference}"/>
<!-- SICURO - controlla esistenza -->
<img data-sly-test="${properties.fileReference}"
src="${properties.fileReference}"
alt="${properties.altText}"/>❌ Problema 4: Filter scorretto
<!-- SBAGLIATO - filter su immagini ma root non è DAM -->
<image
rootPath="/content/mysite"
filter="image"/>
<!-- CORRETTO - root su DAM per immagini -->
<image
rootPath="/content/dam"
filter="image"/>Best Practices
- ✅ rootPath specifico: Limita sempre a un sottopercorso logico
- ✅ filter appropriato: Usa filter per guidare la selezione
- ✅ required per assets critici: Immagine hero, logo, etc.
- ✅ fieldDescription: Spiega cosa selezionare
- ✅ Validazione HTL: Controlla sempre che il path esista
- ✅ .html per pagine: Aggiungi estensione nei link
- ✅ Alt text obbligatorio: Per immagini, richiedi sempre alt text
- ✅ Link Externalizer: Usa in Model per URL esterni
PathBrowser vs FileUpload
| PathBrowser | FileUpload | |
|---|---|---|
| Uso | Seleziona contenuto esistente | Carica nuovo file |
| Source | DAM / Content | Locale dell'autore |
| Quando | Immagini già nel DAM | Upload nuove immagini |
Regola: Se il contenuto è già in AEM, usa PathBrowser!
Pattern Avanzati
1. Link Esterno o Interno
<!-- Checkbox: Link esterno? -->
<isExternalLink
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
name="./isExternalLink"
text="External link"
value="{Boolean}true"
uncheckedValue="{Boolean}false"/>
<!-- PathBrowser: Link interno (se non esterno) -->
<internalLink
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Internal Link"
name="./internalLink"
rootPath="/content/mysite"
filter="hierarchyNotFile"
granite:hide="${properties.isExternalLink}"/>
<!-- Textfield: URL esterno (se esterno) -->
<externalUrl
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="External URL"
name="./externalUrl"
granite:hide="${!properties.isExternalLink}"/>Sling Model:
public String getLinkUrl() {
if (isExternalLink && externalUrl != null) {
return externalUrl;
} else if (internalLink != null) {
return internalLink + ".html";
}
return null;
}2. Immagine con Renditions
<picture>
<source srcset="${properties.fileReference}./_jcr_content/renditions/cq5dam.web.1280.1280.jpeg"
media="(min-width: 1024px)"/>
<source srcset="${properties.fileReference}./_jcr_content/renditions/cq5dam.web.640.640.jpeg"
media="(min-width: 640px)"/>
<img src="${properties.fileReference}"
alt="${properties.altText}"/>
</picture>Prossima Lezione
Nella prossima lezione vedremo il componente RichText Editor per contenuti formattati.
Risorse:
Guida #5 della serie AEM Dialog Components - ← Lezione precedente | Prossima lezione →