AEM Dialog Components #1: Textfield - Input Testo Singola Riga

AEM Dialog Components #1: Textfield

Cos'è un Textfield?

Il Textfield è un componente Granite UI che permette agli autori di inserire testo su una singola riga nelle dialog dei componenti AEM.

Casi d'uso comuni:

  • Titoli e heading
  • URL o link
  • Nome autore
  • ID o codici
  • Email, telefono
  • Qualsiasi input testuale breve

Configurazione Base

XML Dialog Minimo

<title
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Title"
    name="./title"/>

Proprietà essenziali:

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

Proprietà Principali

1. Required (Campo Obbligatorio)

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

Comportamento: L'autore non può salvare la dialog senza compilare il campo.


2. Default Value (Valore Predefinito)

<title
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Title"
    name="./title"
    value="Default Title"/>

Quando usarlo: Fornire un valore iniziale che l'autore può modificare.


3. Placeholder

<email
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Email"
    name="./email"
    emptyText="esempio@dominio.com"/>

Comportamento: Mostra un testo suggeritivo quando il campo è vuoto.


4. Maxlength (Lunghezza Massima)

<shortTitle
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Short Title"
    name="./shortTitle"
    maxlength="{Long}50"/>

Importante: Usa {Long} per il type hint del numero!


5. Validation (Validazione con Regex)

IMPORTANTE: Non esiste un attributo validation="email" in Granite UI. Per validare email, URL o altri pattern devi usare granite:data con regex.

Validazione Email

<email
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Email"
    name="./email"
    required="{Boolean}true">
    <granite:data
        jcr:primaryType="nt:unstructured"
        validation-regex="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
        validation-regex-message="Inserisci un indirizzo email valido"/>
</email>

Validazione Custom con Regex

<phoneNumber
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Phone Number"
    name="./phoneNumber"
    required="{Boolean}true">
    <granite:data
        jcr:primaryType="nt:unstructured"
        validation-regex="^[0-9]{10}$"
        validation-regex-message="Inserisci esattamente 10 cifre"/>
</phoneNumber>

Nota: La sintassi del regex varia leggermente tra AEM 6.3 e AEM 6.4+ per l'escaping dei backslash.


6. Disabled e ReadOnly

<!-- Disabled: campo disabilitato, non editabile -->
<id
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Component ID"
    name="./id"
    disabled="{Boolean}true"/>

<!-- ReadOnly: visibile ma non editabile -->
<createdBy
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Created By"
    name="./createdBy"
    readOnly="{Boolean}true"/>

Differenza:

  • disabled - Campo grigio, non inviato al server
  • readOnly - Campo normale visivamente ma non editabile

Esempi Pratici

Esempio 1: Titolo con Lunghezza Massima

<title
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Title"
    name="./title"
    required="{Boolean}true"
    maxlength="{Long}120"
    emptyText="Enter component title (max 120 chars)"/>

Use case: Titoli per SEO o card con limite caratteri.


Esempio 2: Email con Validazione

<email
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Email Address"
    name="./email"
    fieldDescription="Contact email for this component"
    required="{Boolean}true"
    emptyText="user@example.com">
    <granite:data
        jcr:primaryType="nt:unstructured"
        validation-regex="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
        validation-regex-message="Inserisci un indirizzo email valido"/>
</email>

Use case: Form di contatto, author info.


Esempio 3: URL con Validazione

<externalLink
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="External Link"
    name="./externalLink"
    fieldDescription="Full URL including https://"
    required="{Boolean}true"
    emptyText="https://example.com">
    <granite:data
        jcr:primaryType="nt:unstructured"
        validation-regex="^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/.*)?$"
        validation-regex-message="Inserisci un URL valido (http:// o https://)"/>
</externalLink>

Use case: Link esterni, integrazioni API.


Esempio 4: CSS Class personalizzata

<cssClass
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Custom CSS Class"
    name="./cssClass"
    fieldDescription="Additional CSS classes (space-separated)"
    emptyText="my-class another-class"/>

Use case: Permettere agli autori di aggiungere classi CSS custom.


Esempio 5: Codice Tracking/Analytics

<trackingId
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Tracking ID"
    name="./trackingId"
    fieldDescription="Google Analytics tracking ID"
    required="{Boolean}true"
    emptyText="UA-123456-1">
    <granite:data
        jcr:primaryType="nt:unstructured"
        validation-regex="^UA-[0-9]+-[0-9]+$"
        validation-regex-message="Formato GA non valido (UA-XXXXXX-X)"/>
</trackingId>

Use case: Configurazione analytics, tracking codes.


Dialog Completa con Textfield

<?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 Component"
    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
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Content"
                        sling:resourceType="granite/ui/components/coral/foundation/container">
                        <items jcr:primaryType="nt:unstructured">

                            <!-- Title Field -->
                            <title
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Title"
                                name="./title"
                                required="{Boolean}true"
                                maxlength="{Long}120"/>

                            <!-- Subtitle Field -->
                            <subtitle
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Subtitle"
                                name="./subtitle"
                                maxlength="{Long}200"
                                emptyText="Optional subtitle"/>

                            <!-- CTA Button Text -->
                            <ctaText
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Button Text"
                                name="./ctaText"
                                value="Learn More"
                                maxlength="{Long}30"/>

                            <!-- CTA Link -->
                            <ctaLink
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Button Link"
                                name="./ctaLink"
                                emptyText="/content/mysite/page"/>

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

Uso in HTL

Lettura Valore Semplice

<h1 class="hero-title">${properties.title}</h1>
<p class="hero-subtitle">${properties.subtitle}</p>

<a href="${properties.ctaLink}" class="btn">
  ${properties.ctaText}
</a>

Con Fallback e Validazione

<!-- Titolo con fallback -->
<h1 data-sly-test="${properties.title}">
  ${properties.title}
</h1>
<h1 data-sly-test="${!properties.title}">
  Default Title
</h1>

<!-- CTA solo se link è presente -->
<a data-sly-test="${properties.ctaLink}"
   href="${properties.ctaLink}"
   class="btn">
  ${properties.ctaText || 'Read More'}
</a>

Con Sling Model

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

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String subtitle;

    @ValueMapValue
    private String ctaText;

    @ValueMapValue
    private String ctaLink;

    public String getTitle() {
        return title != null ? title : "Default Title";
    }

    public String getSubtitle() {
        return subtitle;
    }

    public String getCtaText() {
        return ctaText != null ? ctaText : "Learn More";
    }

    public String getCtaLink() {
        return ctaLink;
    }

    public boolean hasSubtitle() {
        return subtitle != null && !subtitle.isEmpty();
    }

    public boolean hasCtaLink() {
        return ctaLink != null && !ctaLink.isEmpty();
    }
}

HTL con Model:

<div data-sly-use.model="com.mysite.models.HeroModel" class="hero">
  <h1 class="hero-title">${model.title}</h1>

  <p data-sly-test="${model.hasSubtitle}" class="hero-subtitle">
    ${model.subtitle}
  </p>

  <a data-sly-test="${model.hasCtaLink}"
     href="${model.ctaLink}"
     class="hero-btn">
    ${model.ctaText}
  </a>
</div>

Proprietà Avanzate

1. fieldDescription (Descrizione Campo)

<apiKey
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="API Key"
    name="./apiKey"
    fieldDescription="Enter your API key from the admin panel"
    required="{Boolean}true"/>

Mostra un testo di aiuto sotto il campo.


2. granite:class (CSS personalizzata)

<customField
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Custom Field"
    name="./customField"
    granite:class="custom-textfield-style"/>

Aggiunge classi CSS custom al campo.


3. autocomplete

<username
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    fieldLabel="Username"
    name="./username"
    autocomplete="username"/>

Valori comuni: username, email, name, tel, url.


Problemi Comuni

❌ Problema 1: Valore non salvato

<!-- SBAGLIATO - manca ./ nel name -->
<title
    name="title"/>

<!-- CORRETTO -->
<title
    name="./title"/>

Soluzione: Usa sempre ./ prefix nel name.


❌ Problema 2: Validazione con sintassi sbagliata

<!-- SBAGLIATO - validation="email" non esiste -->
<email
    validation="email"
    required="{Boolean}true"/>

<!-- CORRETTO - usa granite:data con regex -->
<email
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
    name="./email"
    required="{Boolean}true">
    <granite:data
        jcr:primaryType="nt:unstructured"
        validation-regex="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
        validation-regex-message="Email non valida"/>
</email>

Soluzione: Non esiste validation="email". Usa granite:data con validation-regex.


❌ Problema 3: Maxlength ignorato

<!-- SBAGLIATO - maxlength senza type hint -->
<title
    maxlength="50"/>

<!-- CORRETTO -->
<title
    maxlength="{Long}50"/>

Soluzione: Usa sempre {Long} per valori numerici.


❌ Problema 4: Required non funziona

<!-- SBAGLIATO - required come stringa -->
<title
    required="true"/>

<!-- CORRETTO -->
<title
    required="{Boolean}true"/>

Soluzione: Usa {Boolean}true, non stringa.


Best Practices

  1. Usa sempre ./ nel name: name="./title"
  2. Type hints per boolean e numeri: required="{Boolean}true", maxlength="{Long}50"
  3. EmptyText per UX: Fornisci esempi nel placeholder
  4. Maxlength per limiti: Previeni input troppo lunghi
  5. FieldDescription per complessità: Spiega campi non ovvi
  6. Validazione per dati critici: Email, URL, pattern specifici
  7. Required solo se necessario: Non forzare campi opzionali
  8. Default value utili: Velocizza l'authoring

Quando NON Usare Textfield

Non usare textfield quando:

  • Serve testo multi-riga → Usa Textarea
  • Serve testo formattato → Usa RichText Editor
  • Serve selezione da opzioni → Usa Select o Radio
  • Serve selezione percorso AEM → Usa PathBrowser
  • Serve numero con spinner → Usa NumberField
  • Serve data → Usa DatePicker
  • Serve colore → Usa ColorField

Prossima Lezione

Nella prossima lezione vedremo il componente Textarea per testo multi-riga.

Risorse:


Guida #1 della serie AEM Dialog Components. Lezione successiva →