AEM Dialog Components #6: RichText Editor - Contenuti Formattati
AEM Dialog Components #6: RichText Editor (RTE)
Cos'è il RichText Editor?
Il RichText Editor (RTE) è un componente Granite UI che permette agli autori di creare contenuti formattati con bold, italic, liste, link, tabelle e molto altro.
Casi d'uso comuni:
- Contenuti editoriali
- Descrizioni lunghe con formattazione
- Articoli e blog posts
- Testi con link interni
- Contenuti con liste e tabelle
Configurazione Base
XML Dialog Minimo
<text
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Text"
name="./text"
useFixedInlineToolbar="{Boolean}true"/>Proprietà essenziali:
sling:resourceType- Semprecq/gui/components/authoring/dialog/richtextfieldLabel- Etichettaname- Percorso JCR (usa sempre./)useFixedInlineToolbar- Toolbar inline fissa (raccomandato)
Proprietà Principali
1. useFixedInlineToolbar
<text
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Text"
name="./text"
useFixedInlineToolbar="{Boolean}true"/>Comportamento:
true- Toolbar sempre visibile in alto (UX migliore)false- Toolbar appare al sele
zione testo (default)
Best practice: Usa sempre true per authoring migliore!
2. Required
<text
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Article Content"
name="./text"
required="{Boolean}true"
useFixedInlineToolbar="{Boolean}true"/>Configurazione Plugin RTE
Il RTE è configurato tramite plugin che abilitano feature specifiche. Ogni plugin va dichiarato sotto il nodo <rtePlugins>.
Struttura Base con Plugin
<text
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Text"
name="./text"
useFixedInlineToolbar="{Boolean}true">
<!-- UI Configuration -->
<uiSettings jcr:primaryType="nt:unstructured">
<cui jcr:primaryType="nt:unstructured">
<!-- Inline toolbar -->
<inline
jcr:primaryType="nt:unstructured"
toolbar="[format#bold,format#italic,format#underline,links#modifylink,lists#unordered,lists#ordered]">
<popovers jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
ref="format"/>
<lists
jcr:primaryType="nt:unstructured"
ref="lists"/>
<links
jcr:primaryType="nt:unstructured"
ref="links"/>
</popovers>
</inline>
</cui>
</uiSettings>
<!-- RTE Plugins -->
<rtePlugins jcr:primaryType="nt:unstructured">
<!-- Format (Bold, Italic, Underline) -->
<format
jcr:primaryType="nt:unstructured"
features="[bold,italic,underline]"/>
<!-- Lists -->
<lists
jcr:primaryType="nt:unstructured"
features="[ordered,unordered]"/>
<!-- Links -->
<links
jcr:primaryType="nt:unstructured"
features="[modifylink,unlink]"/>
</rtePlugins>
</text>Plugin Disponibili
1. Format (Bold, Italic, Underline)
<format
jcr:primaryType="nt:unstructured"
features="[bold,italic,underline]"/>Genera: <strong>, <em>, <u>
2. Lists (Ordered, Unordered)
<lists
jcr:primaryType="nt:unstructured"
features="[ordered,unordered,indent,outdent]"/>Genera: <ul>, <ol>, <li>
3. Links (Modifica, Rimuovi)
<links
jcr:primaryType="nt:unstructured"
features="[modifylink,unlink]">
<linkDialogConfig jcr:primaryType="nt:unstructured">
<linkDialogFields jcr:primaryType="nt:unstructured">
<targetToggle
jcr:primaryType="nt:unstructured"
checked="{Boolean}false"/>
</linkDialogFields>
</linkDialogConfig>
</links>Features:
modifylink- Crea/modifica linkunlink- Rimuovi link
Genera: <a href="...">
4. Justify (Allineamento)
<justify
jcr:primaryType="nt:unstructured"
features="[justifyleft,justifycenter,justifyright]"/>5. Paraformat (Heading, Paragrafi)
<paraformat
jcr:primaryType="nt:unstructured"
features="*">
<formats jcr:primaryType="nt:unstructured">
<p
jcr:primaryType="nt:unstructured"
description="Paragraph"
tag="p"/>
<h2
jcr:primaryType="nt:unstructured"
description="Heading 2"
tag="h2"/>
<h3
jcr:primaryType="nt:unstructured"
description="Heading 3"
tag="h3"/>
<h4
jcr:primaryType="nt:unstructured"
description="Heading 4"
tag="h4"/>
</formats>
</paraformat>Genera: <h2>, <h3>, <h4>, <p>
6. Subsuperscript (Pedice, Apice)
<subsuperscript
jcr:primaryType="nt:unstructured"
features="[subscript,superscript]"/>Genera: <sub>, <sup>
7. Table (Tabelle)
<table
jcr:primaryType="nt:unstructured"
features="[createtable,removetable,insertrow,removerow,insertcolumn,removecolumn,cellprops,mergecells,splitcell,selectrow,selectcolumn]"/>Genera: <table>, <tr>, <td>, <th>
8. Misctools (Codice, Cerca/Sostituisci)
<misctools
jcr:primaryType="nt:unstructured"
features="[sourceedit,findreplace]"/>sourceedit- Modifica HTML rawfindreplace- Cerca e sostituisci
Esempi Pratici
Esempio 1: RTE Semplice (Bold, Italic, Lists, Link)
<description
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Description"
name="./description"
useFixedInlineToolbar="{Boolean}true">
<uiSettings jcr:primaryType="nt:unstructured">
<cui jcr:primaryType="nt:unstructured">
<inline
jcr:primaryType="nt:unstructured"
toolbar="[format#bold,format#italic,links#modifylink,lists#unordered,lists#ordered]">
<popovers jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
ref="format"/>
<lists
jcr:primaryType="nt:unstructured"
ref="lists"/>
<links
jcr:primaryType="nt:unstructured"
ref="links"/>
</popovers>
</inline>
</cui>
</uiSettings>
<rtePlugins jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
features="[bold,italic]"/>
<lists
jcr:primaryType="nt:unstructured"
features="[ordered,unordered]"/>
<links
jcr:primaryType="nt:unstructured"
features="[modifylink,unlink]"/>
</rtePlugins>
</description>Esempio 2: RTE Completo (Articolo Editoriale)
<articleText
jcr:primaryType="nt:unstructured"
sling:resourceType="cq/gui/components/authoring/dialog/richtext"
fieldLabel="Article Content"
name="./articleText"
required="{Boolean}true"
useFixedInlineToolbar="{Boolean}true">
<uiSettings jcr:primaryType="nt:unstructured">
<cui jcr:primaryType="nt:unstructured">
<inline
jcr:primaryType="nt:unstructured"
toolbar="[format#bold,format#italic,format#underline,#paraformat,links#modifylink,lists#unordered,lists#ordered,justify#justifyleft,justify#justifycenter,justify#justifyright]">
<popovers jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
ref="format"/>
<paraformat
jcr:primaryType="nt:unstructured"
ref="paraformat"/>
<lists
jcr:primaryType="nt:unstructured"
ref="lists"/>
<links
jcr:primaryType="nt:unstructured"
ref="links"/>
<justify
jcr:primaryType="nt:unstructured"
ref="justify"/>
</popovers>
</inline>
</cui>
</uiSettings>
<rtePlugins jcr:primaryType="nt:unstructured">
<format
jcr:primaryType="nt:unstructured"
features="[bold,italic,underline]"/>
<paraformat
jcr:primaryType="nt:unstructured"
features="*">
<formats jcr:primaryType="nt:unstructured">
<p
jcr:primaryType="nt:unstructured"
description="Paragraph"
tag="p"/>
<h2
jcr:primaryType="nt:unstructured"
description="Heading 2"
tag="h2"/>
<h3
jcr:primaryType="nt:unstructured"
description="Heading 3"
tag="h3"/>
<h4
jcr:primaryType="nt:unstructured"
description="Heading 4"
tag="h4"/>
</formats>
</paraformat>
<lists
jcr:primaryType="nt:unstructured"
features="[ordered,unordered,indent,outdent]"/>
<links
jcr:primaryType="nt:unstructured"
features="[modifylink,unlink]"/>
<justify
jcr:primaryType="nt:unstructured"
features="[justifyleft,justifycenter,justifyright]"/>
</rtePlugins>
</articleText>Uso in HTL
Rendering HTML
<!-- RTE genera già HTML, usa context='html' -->
<div class="article-content">
${properties.text }
</div>⚠️ IMPORTANTE: Usa sempre @ context='html' per il RTE!
Con Validazione
<div data-sly-test="${properties.text}" class="content">
${properties.text }
</div>
<!-- Oppure con fallback -->
<div class="content">
${properties.text || '<p>No content available</p>'}
</div>Con Sling Model
@Model(adaptables = Resource.class)
public class ArticleModel {
@ValueMapValue
private String articleText;
public String getArticleText() {
return articleText;
}
public boolean hasContent() {
return articleText != null && !articleText.isEmpty();
}
/**
* Strip HTML per excerpt
*/
public String getPlainTextExcerpt(int maxLength) {
if (articleText == null) {
return "";
}
// Strip HTML tags
String plainText = articleText.replaceAll("<[^>]*>", "");
// Truncate
if (plainText.length() > maxLength) {
return plainText.substring(0, maxLength) + "...";
}
return plainText;
}
/**
* Word count
*/
public int getWordCount() {
if (articleText == null) {
return 0;
}
String plainText = articleText.replaceAll("<[^>]*>", "");
return plainText.split("\\s+").length;
}
}HTL con Model:
<article data-sly-use.model="com.mysite.models.ArticleModel">
<div data-sly-test="${model.hasContent}" class="article-body">
${model.articleText }
</div>
<div class="article-meta">
<span>Word count: ${model.wordCount}</span>
</div>
<!-- Plain text excerpt per SEO -->
<meta name="description" content="${model.getPlainTextExcerpt(160)}"/>
</article>RTE vs Textarea
| RTE | Textarea | |
|---|---|---|
| Formattazione | Sì (bold, link, liste) | No (testo puro) |
| HTML Output | Sì | No |
| Complessità | Alta | Bassa |
| Performance | Più pesante | Leggero |
| Use Case | Contenuti editoriali | Testi semplici, metadata |
Regola: Usa RTE solo se serve formattazione!
Problemi Comuni
❌ Problema 1: HTML non renderizzato
<!-- SBAGLIATO - mostra tag HTML come testo -->
<div>${properties.text}</div>
<!-- CORRETTO - renderizza HTML -->
<div>${properties.text }</div>❌ Problema 2: Plugin non funzionante
<!-- SBAGLIATO - plugin dichiarato ma non nella toolbar -->
<rtePlugins>
<format features="[bold,italic]"/>
</rtePlugins>
<!-- Toolbar non include format#bold -->
<inline toolbar="[lists#unordered]"/>
<!-- CORRETTO - toolbar include i plugin -->
<inline toolbar="[format#bold,format#italic,lists#unordered]"/>❌ Problema 3: Link rotti per pagine interne
Problema: RTE salva link come /content/mysite/page senza .html
Soluzione in HTL: Post-process il contenuto
public String getProcessedText() {
if (articleText == null) {
return "";
}
// Add .html to internal links
return articleText.replaceAll(
"href=\"(/content/[^\"]+)\"",
"href=\"$1.html\""
);
}❌ Problema 4: HTML non valido salvato
Problema: Autori copiano HTML da Word/Email
Soluzione: Usa plugin paste con opzioni di cleanup
<paste
jcr:primaryType="nt:unstructured"
features="[plaintext,wordhtml,markdown]"
defaultPasteMode="plaintext"/>Best Practices
- ✅ useFixedInlineToolbar: Sempre
trueper UX migliore - ✅ Solo plugin necessari: Non abilitare tutto
- ✅ Heading gerarchici: h2, h3, h4 (non h1)
- ✅ context='html' in HTL: Sempre per RTE
- ✅ Limita formattazione: Bold, italic, liste, link sono sufficienti
- ✅ Test copia-incolla: Verifica comportamento con Word/Google Docs
- ✅ Validazione server-side: Sanifica HTML nel backend
- ✅ Accessibility: Configura plugin per HTML semantico
Configurazione Globale RTE
Puoi configurare l'RTE globalmente in:
/apps/cq/gui/components/authoring/editors/clientlibs/core/rte/config.json
Questo permette configurazioni riutilizzabili across tutti i componenti.
Plugin Avanzati
Styles (Classi CSS Custom)
<styles
jcr:primaryType="nt:unstructured"
features="*">
<styles jcr:primaryType="nt:unstructured">
<highlight
jcr:primaryType="nt:unstructured"
cssName="text-highlight"
text="Highlight"/>
<quote
jcr:primaryType="nt:unstructured"
cssName="blockquote"
text="Quote"
tag="blockquote"/>
</styles>
</styles>Prossima Lezione
Questa conclude la serie base sui componenti dialog di AEM! Nelle prossime guide vedremo componenti avanzati come FileUpload, ColorField, NumberField e altri.
Risorse:
Guida #6 della serie AEM Dialog Components - ← Lezione precedente
Serie completa: Textfield | Textarea | Checkbox | Select | PathBrowser | RichText