AEM Dialog Components #2: Textarea - Input Testo Multi-Riga

AEM Dialog Components #2: Textarea

Cos'è un Textarea?

Il Textarea è un componente Granite UI che permette agli autori di inserire testo su più righe nelle dialog dei componenti AEM.

Differenza con Textfield:

  • Textfield → Una singola riga
  • Textarea → Più righe, con a capo

Casi d'uso comuni:

  • Descrizioni e abstract
  • Note e commenti
  • Metadata e alt text
  • Code snippets semplici
  • Disclaimer e note legali

Configurazione Base

XML Dialog Minimo

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"/>

Proprietà essenziali:

  • sling:resourceType - Sempre granite/ui/components/coral/foundation/form/textarea
  • fieldLabel - Etichetta visibile all'autore
  • name - Percorso JCR dove salvare il valore (usa sempre ./)

Proprietà Principali

1. Required (Campo Obbligatorio)

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"
    required="{Boolean}true"/>

2. Rows (Altezza in Righe)

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"
    rows="{Long}5"/>

Default: 2 righe

Best practice:

  • 3-5 righe per descrizioni brevi
  • 8-12 righe per testo lungo
  • 15+ righe per code o contenuti estesi

3. Maxlength (Lunghezza Massima)

<metaDescription
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Meta Description"
    name="./metaDescription"
    maxlength="{Long}160"
    rows="{Long}3"
    fieldDescription="Max 160 characters for SEO"/>

Importante: Come textfield, usa {Long} per il type hint!


4. EmptyText (Placeholder)

<notes
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Internal Notes"
    name="./notes"
    emptyText="Add notes for editors..."
    rows="{Long}4"/>

5. Resize (Ridimensionamento)

<content
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Content"
    name="./content"
    resize="vertical"
    rows="{Long}8"/>

Valori:

  • vertical - Ridimensionabile verticalmente (default)
  • both - Ridimensionabile verticalmente e orizzontalmente
  • none - Dimensione fissa

Esempi Pratici

Esempio 1: Meta Description (SEO)

<metaDescription
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Meta Description"
    name="./metaDescription"
    fieldDescription="SEO description (max 160 chars)"
    required="{Boolean}true"
    maxlength="{Long}160"
    rows="{Long}3"
    emptyText="Enter a concise description for search engines..."/>

Use case: Ottimizzazione SEO delle pagine.


Esempio 2: Alt Text per Immagini

<altText
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Alt Text"
    name="./altText"
    fieldDescription="Describe the image for accessibility"
    required="{Boolean}true"
    maxlength="{Long}125"
    rows="{Long}2"
    emptyText="Describe what is shown in the image"/>

Use case: Accessibilità e SEO per le immagini.


Esempio 3: Note Interne per Editori

<editorNotes
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Editor Notes"
    name="./editorNotes"
    fieldDescription="Internal notes (not displayed on the site)"
    rows="{Long}5"
    emptyText="Add notes for other editors..."/>

Use case: Workflow e comunicazione tra autori.


Esempio 4: Descrizione Prodotto

<productDescription
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Product Description"
    name="./productDescription"
    required="{Boolean}true"
    rows="{Long}8"
    maxlength="{Long}500"
    emptyText="Enter a detailed product description..."
    resize="vertical"/>

Use case: E-commerce, schede prodotto.


Esempio 5: Codice Embed (Script, iframe)

<embedCode
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Embed Code"
    name="./embedCode"
    fieldDescription="Paste third-party embed code (YouTube, Twitter, etc.)"
    rows="{Long}10"
    resize="vertical"
    emptyText="<iframe src='...'></iframe>"/>

Use case: Integrazione contenuti esterni (video, social, mappe).


Dialog Completa con Textarea

<?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 (Textfield) -->
                            <title
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Title"
                                name="./title"
                                required="{Boolean}true"/>

                            <!-- Description (Textarea) -->
                            <description
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                fieldLabel="Description"
                                name="./description"
                                rows="{Long}5"
                                maxlength="{Long}300"
                                emptyText="Enter card description..."/>

                        </items>
                    </content>

                    <!-- SEO Tab -->
                    <seo
                        jcr:primaryType="nt:unstructured"
                        jcr:title="SEO"
                        sling:resourceType="granite/ui/components/coral/foundation/container">
                        <items jcr:primaryType="nt:unstructured">

                            <!-- Meta Description -->
                            <metaDescription
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                fieldLabel="Meta Description"
                                name="./metaDescription"
                                fieldDescription="SEO description (max 160 chars)"
                                maxlength="{Long}160"
                                rows="{Long}3"/>

                            <!-- Alt Text -->
                            <altText
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                fieldLabel="Image Alt Text"
                                name="./altText"
                                maxlength="{Long}125"
                                rows="{Long}2"/>

                        </items>
                    </seo>

                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

Uso in HTL

Lettura Valore Semplice

<div class="card-description">
  ${properties.description}
</div>

Importante: Il testo avrà gli "a capo" (\n) che HTML ignora. Per preservarli:

<div class="card-description" style="white-space: pre-line;">
  ${properties.description}
</div>

Oppure converti \n in <br>:

<div class="card-description">
  ${properties.description @ context='html'}
</div>

Con Fallback

<div data-sly-test="${properties.description}" class="description">
  ${properties.description}
</div>

<!-- Oppure con operatore ternario -->
<div class="description">
  ${properties.description || 'No description available'}
</div>

Tronca Testo con CSS

<div class="description truncate-3-lines">
  ${properties.description}
</div>

CSS:

.truncate-3-lines {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

Con Sling Model

@Model(adaptables = Resource.class)
public class ContentCardModel {

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String description;

    @ValueMapValue
    private String metaDescription;

    public String getTitle() {
        return title;
    }

    public String getDescription() {
        return description;
    }

    /**
     * Descrizione troncata a N caratteri per preview
     */
    public String getShortDescription(int maxLength) {
        if (description == null || description.length() <= maxLength) {
            return description;
        }
        return description.substring(0, maxLength) + "...";
    }

    /**
     * Meta description con fallback su description
     */
    public String getMetaDescription() {
        if (metaDescription != null && !metaDescription.isEmpty()) {
            return metaDescription;
        }
        // Fallback: usa primi 160 caratteri della description
        return getShortDescription(160);
    }

    /**
     * Converte \n in <br> per HTML
     */
    public String getDescriptionAsHtml() {
        if (description == null) {
            return "";
        }
        return description.replace("\n", "<br>");
    }

    public boolean hasDescription() {
        return description != null && !description.isEmpty();
    }
}

HTL con Model:

<div data-sly-use.model="com.mysite.models.ContentCardModel" class="card">

  <h3 class="card-title">${model.title}</h3>

  <!-- Descrizione completa -->
  <div data-sly-test="${model.hasDescription}" class="card-description">
    ${model.description}
  </div>

  <!-- Oppure descrizione troncata -->
  <div class="card-preview">
    ${model.getShortDescription(150)}
  </div>

  <!-- Meta tag per SEO (in <head>) -->
  <meta name="description" content="${model.metaDescription}"/>

</div>

Textarea vs RichText Editor

Quando Usare Textarea

Usa Textarea quando:

  • Testo semplice senza formattazione
  • Limiti di lunghezza stretti (es. meta description)
  • Performance è importante
  • Non serve bold, italic, link, etc.
  • Alt text, note, metadata

Quando Usare RichText Editor

Usa RichText quando:

  • Serve formattazione (bold, italic, lists)
  • Serve inserire link
  • Contenuto editoriale lungo
  • Autori hanno bisogno di flessibilità

Regola pratica: Se l'autore non ha bisogno di formattare, usa Textarea!


Proprietà Avanzate

1. autofocus

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"
    autofocus="{Boolean}true"/>

Focus automatico all'apertura della dialog.


2. disabled e readOnly

<!-- Disabled -->
<generatedText
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Auto-Generated Text"
    name="./generatedText"
    disabled="{Boolean}true"
    rows="{Long}4"/>

<!-- ReadOnly -->
<originalText
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Original Text"
    name="./originalText"
    readOnly="{Boolean}true"
    rows="{Long}6"/>

3. granite:class

<customTextarea
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Custom Textarea"
    name="./customTextarea"
    granite:class="monospace-font"
    rows="{Long}10"/>

Aggiungi CSS custom (es. monospace per codice).


Problemi Comuni

❌ Problema 1: A capo non visualizzati

<!-- SBAGLIATO - HTML ignora \n -->
<div>${properties.description}</div>

<!-- CORRETTO - Preserva a capo -->
<div style="white-space: pre-line;">
  ${properties.description}
</div>

<!-- OPPURE converti \n in <br> nel Sling Model -->
<div>${model.descriptionAsHtml @ context='html'}</div>

❌ Problema 2: Maxlength ignorato

<!-- SBAGLIATO -->
<description maxlength="160"/>

<!-- CORRETTO -->
<description maxlength="{Long}160"/>

Usa sempre {Long} per numeri.


❌ Problema 3: Textarea troppo piccola

<!-- SBAGLIATO - default 2 righe, troppo piccolo -->
<description
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    name="./description"/>

<!-- CORRETTO - imposta rows appropriate -->
<description
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    name="./description"
    rows="{Long}5"/>

❌ Problema 4: Testo troncato visivamente

Se il testo appare troncato nel component ma nel JCR è completo:

Causa: CSS che limita l'altezza del container Soluzione: Verifica CSS e aggiungi overflow: auto o white-space: pre-line


Best Practices

  1. Rows appropriate: 3-5 per descrizioni, 8-12 per testo lungo
  2. Maxlength per SEO: 160 per meta description, 125 per alt text
  3. EmptyText esplicativo: Dai esempi chiari
  4. FieldDescription: Spiega limiti e uso
  5. Resize vertical: Permetti ridimensionamento per testi lunghi
  6. Preserva a capo in HTL: Usa white-space: pre-line o converti in <br>
  7. Fallback su RTE: Se serve formattazione, passa a RichText
  8. Validation in Sling Model: Controlla lunghezza e contenuto lato server

Quando NON Usare Textarea

Non usare textarea quando:

  • Serve una singola riga → Usa Textfield
  • Serve formattazione (bold, link) → Usa RichText Editor
  • Serve codice complesso → Usa Code Editor (custom)
  • Serve selezione da lista → Usa Select o Radio

Prossima Lezione

Nella prossima lezione vedremo il componente Checkbox per selezioni boolean.

Risorse:


Guida #2 della serie AEM Dialog Components - ← Lezione precedente | Prossima lezione →