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- Sempregranite/ui/components/coral/foundation/form/textfieldfieldLabel- Etichetta visibile all'autorename- 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 serverreadOnly- 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
- ✅ Usa sempre
./nel name:name="./title" - ✅ Type hints per boolean e numeri:
required="{Boolean}true",maxlength="{Long}50" - ✅ EmptyText per UX: Fornisci esempi nel placeholder
- ✅ Maxlength per limiti: Previeni input troppo lunghi
- ✅ FieldDescription per complessità: Spiega campi non ovvi
- ✅ Validazione per dati critici: Email, URL, pattern specifici
- ✅ Required solo se necessario: Non forzare campi opzionali
- ✅ 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 →