Snippets, componenti e layout
Gli Snippets sono blocchi template riutilizzabili. Ci sono quattro modi di usarli – dalla semplice inclusione fino al componente con slot.
7.1 Includere con include
{% include "header" %}
{% include "teaser" with titel="Neu bei uns" link="/aktuelles" %}
Lo snippet vede tutte le variabili della pagina più i valori passati. Le modifiche allo snippet valgono subito su tutte le pagine che lo includono. I nomi degli snippet possono contenere barre, ad es. teile/footer. Un nuovo snippet con un nome di questo tipo finisce automaticamente nell’elenco nella cartella teile. La cartella puoi anche impostarla liberamente accanto al nome; serve solo per la panoramica.
7.2 Componenti con slot
Un componente è uno snippet che può racchiudere contenuto – come i componenti in Vue o Svelte, solo lato server. Lo snippet stabilisce con {% slot %} dove appare il contenuto; quando lo usi riempi gli slot.
Lo snippet card:
<div class="card {{ variante|default:"normal" }}">
<h3>{% slot "titel" %}Ohne Titel{% endslot %}</h3>
<div class="card-body">{% slot %}{% endslot %}</div>
{% if slots.footer %}
<footer>{% slot "footer" %}{% endslot %}</footer>
{% endif %}
</div>
Uso:
{% component "card" variante="hervorgehoben" %}
{% fill "titel" %}Unser <em>Angebot</em>{% endfill %}
<p>Alles, was nicht in einem fill steht, landet im Standard-Slot.</p>
{% fill "footer" %}<a href="/kontakt">Jetzt anfragen</a>{% endfill %}
{% endcomponent %}
Le regole:
| Elemento | Significato |
|---|---|
{% slot %}…{% endslot %} |
Slot predefinito nello snippet. Il contenuto tra i tag è il fallback se non viene passato nulla. |
{% slot "name" %}…{% endslot %} |
Slot con nome con fallback. |
{% fill "name" %}…{% endfill %} |
Riempie uno slot con nome quando lo usi. |
Contenuto fuori da fill |
Riempie lo slot predefinito. |
slots.name / slots.default |
Nello snippet: true se lo slot è stato riempito – per wrapper facoltativi. |
name=wert dopo il nome dello snippet |
Parametri (props), disponibili nello snippet come variabili. |
Da sapere:
-
Il contenuto vede le variabili della pagina. Ciò che sta tra
componentedendcomponentviene renderizzato nel contesto della pagina chiamante – le variabili di ciclo funzionano:{% for post in data("blog") %} {% component "card" %} {% fill "titel" %}{{ post.titel }}{% endfill %} {{ post.text|markdown }} {% endcomponent %} {% endfor %} -
Lo snippet vede solo i suoi parametri più le variabili globali (
page,route,nav(),data()…), ma nessuna variabile locale della pagina. Ciò di cui il componente ha bisogno lo passi esplicitamente come parametro. Questo rende i componenti prevedibili e riutilizzabili. -
L’HTML come parametro resta HTML se è contrassegnato come sicuro:
text=post.text|markdownoinhalt="<b>x</b>"|safe. I testi normali vengono sottoposti a escape come di consueto. -
Annidare è consentito – componenti dentro componenti, anche nei blocchi
fill:{% component "card" %} {% fill "titel" %}Kontakt{% endfill %} {% component "button" href="/kontakt" variante="primaer" %}Schreib uns{% endcomponent %} {% endcomponent %} -
Il nome dello snippet può anche provenire da una variabile:
{% component block.typ %}– utile per costruttori di pagine basati sui dati di una Collection. -
Un componente che richiama se stesso all’infinito viene fermato dopo 30 livelli con un messaggio di errore.
Gli snippet con slot portano nell’elenco degli snippet il badge Componente; nell’editor HTML, Snippet → Componente inserisce la struttura di base con tutti gli slot con nome.
7.3 Layout con extends e block
Un layout stabilisce la struttura di un’intera pagina; la pagina ne riempie solo i blocchi. Lo snippet layouts/standard:
{% include "header" %}
<main class="container">
{% block inhalt %}{% endblock %}
</main>
<aside>{% block seitenleiste %}{% include "standard-seitenleiste" %}{% endblock %}</aside>
{% include "footer" %}
Nell’HTML di una pagina:
{% extends "layouts/standard" %}
{% block inhalt %}
<h1>{{ page.title }}</h1>
<p>…</p>
{% endblock %}
extendsdeve stare proprio all’inizio. Tutto ciò che sta fuori dablockviene ignorato.- I blocchi non sovrascritti mantengono il contenuto del layout (qui la barra laterale standard).
- Per ogni pagina c’è esattamente un layout. Per blocchi ripetuti all’interno di una pagina usa i componenti.
Layout di pagina per le nuove pagine
Uno snippet di layout può offrirsi come modello per le nuove pagine nell’editor degli snippet, scheda Layout di pagina: spunta Offri come layout di pagina, aggiungi nome e descrizione per la scelta e, se vuoi, Preseleziona per le nuove pagine. La finestra Nuova pagina offre allora i layout accanto a Pagina vuota (il comportamento di sempre); una nuova pagina con layout contiene solo
{% extends "layouts/inhaltsseite" %}
Le modifiche al layout valgono così per tutte le pagine che lo usano, e le singole pagine possono sovrascrivere i blocchi come sopra. Accanto a ogni layout IllusionCMS mostra se ha un’area contenuti per i blocchi – cioè se produce {% blocks page.blocks %}, direttamente o tramite extends, include e componenti. I layout senza quest’area portano il chip contenuto statico: la pagina mostra contenuto fisso dal template, lì i blocchi non si possono gestire. È un caso normale e voluto – per esempio una pagina contatti con indirizzo e modulo.
I blocchi non vengono inseriti automaticamente, così mantieni il pieno controllo sul codice della pagina. Può essere voluto scegliere layout senza aree contenuti modificabili.
Integrazioni come IllusionPublish leggono i layout tramite GET /api/v1/page-layouts e creano pagine con layout_id (documentazione API).
7.4 Macro
Per piccoli blocchi con parametri e senza contenuto ci sono le macro. Nello snippet makros (l’export è obbligatorio):
{% macro preis(betrag, waehrung="€") export %}<span class="preis">{{ betrag|floatformat:2 }} {{ waehrung }}</span>{% endmacro %}
{% import "makros" preis %}
{{ preis(19.9) }} → 19.90 €
{{ preis(5, "$") }} → 5.00 $
Nella maggior parte dei casi i componenti sono più flessibili, perché possono accogliere contenuto HTML.
7.5 CSS e JS negli snippet
Nell’editor degli snippet, a destra in CSS & JS di questo snippet, associ file dalla libreria (+ associa) oppure ne crei di nuovi (+ nuovo, nome con .css o .js). Vengono caricati solo nelle pagine che eseguono effettivamente lo snippet durante il rendering – stili e script di uno slider quindi non in ogni pagina, ma solo dove c’è lo slider.
- Conta ciò che viene davvero renderizzato:
{% include %}(anche con nomi da una variabile), componenti, snippet annidati e layout tramiteextends. In un ramo{% if %}che non si applica o in un ciclo vuoto non si carica nulla. - Gli snippet di macro caricano i loro file non appena vengono inclusi con
{% import %}. - Evitare la ricorsione infinita: macro e include ricorsivi (ad es. per strutture ad albero) sono consentiti, ma richiedono sempre una condizione di arresto. Una ricorsione infinita – anche tramite un
includecon variabile che include sé stesso – termina il processo del server, così come valori estremi per{% lorem %}e per i filtricenter,ljust,rjustefloatformat. Quando provi nuove macro, parti con valori piccoli. - Contano solo il contenuto della pagina e il campo Head – uno snippet nel titolo SEO o nella descrizione non carica nulla.
- Il CSS sta nell’
<head>, il JS prima di</body>, in entrambi i casi dopo i file della pagina. Ogni file viene caricato una sola volta – anche se lo snippet compare dieci volte o la pagina ha già lo stesso file. - L’anteprima nell’editor delle pagine mostra la versione salvata dello snippet.
Widget già compilati da Svelte, Vue, React & co.: compili il modulo in un progetto separato (ad es. con Vite) e riprendi il risultato da dist/:
| Dalla build | Diventa in IllusionCMS |
|---|---|
il markup dal <body> di index.html, di solito solo il punto di montaggio (<div id="app"></div>) |
il contenuto dello snippet |
assets/index-….js |
un file JS sullo snippet (+ nuovo → ad es. wetter.js, incolla il contenuto) |
assets/index-….css |
un file CSS sullo snippet |
Poi includi lo snippet ({% include "wetter" %}) – CSS e JS vengono caricati solo lì. Vite genera il JS per <script type="module">. Se il file contiene sintassi di modulo (import/export al livello più alto o import.meta), IllusionCMS lo include automaticamente in questo modo; senza sintassi di modulo come normale <script> – in entrambi i casi senza impostazioni.
Perché la build funzioni senza ritocchi:
- Un solo file JS: se Vite divide il codice in più file (
import(…)per il lazy loading, chunkvendorseparato), gli altri mancano – il file si trova sotto/_assets/js/<id>.js, e gli import relativi lì non trovano nulla. Rinuncia al lazy loading oppure imposta invite.config.jsbuild: { rollupOptions: { output: { inlineDynamicImports: true } } }. - Immagini e font della build: o abbastanza piccoli da essere incorporati (
build.assetsInlineLimit), oppure caricali in File e usali con indirizzo assoluto (/media/…). - Più volte in una pagina: lo script viene caricato una sola volta. Una build che parte in
#appparte quindi una volta sola. Per più istanze, nel punto di ingresso cerca tutti i segnaposto, ad es. con Svelte 5:
// src/main.js
import { mount } from 'svelte'
import Wetter from './Wetter.svelte'
document.querySelectorAll('.wetter-widget').forEach((el) => {
mount(Wetter, { target: el, props: { ort: el.dataset.ort } })
})
Lo snippet restituisce allora <div class="wetter-widget" data-ort="{{ ort|default:"Berlin" }}"></div> – i valori del template arrivano nel widget tramite attributi data-… (gli output con {{ … }} sono sottoposti automaticamente a escape).
- I moduli vengono eseguiti solo quando la pagina è stata analizzata – i segnaposto sono allora già nel DOM.
7.6 Costruttore di pagine: moduli e blocchi
Per le pagine che altri devono gestire senza HTML c’è il costruttore di pagine: costruisci una volta dei moduli (Snippets con uno schema), e la pagina o una voce di collection salva solo dati – un elenco di blocchi, ognuno con il suo modulo, i suoi valori ed eventualmente blocchi annidati. L’HTML resta nei tuoi Snippets; l’editor a moduli non tocca mai il markup.
Creare un modulo
Un modulo è uno Snippet con uno schema. Nell’editor degli Snippets apri la scheda Modulo, fai clic su Definisci come modulo e imposta:
- Etichetta, descrizione, icona e categoria – così il modulo compare in Aggiungi blocco.
- Parametri – strutturati come i campi di una collection: nome, etichetta, tipo, obbligatorio, valore predefinito, le opzioni per Scelta, la collection per Collegamento. La casella Design contrassegna i parametri di aspetto (variante, spaziature, colori); nel modulo stanno in un gruppo Aspetto a parte.
- Slot – punti in cui possono stare altri blocchi, eventualmente limitati a determinati moduli e a un numero massimo.
Con la casella Usa come modulo attiva, il modulo compare nella scelta; nell’elenco degli Snippets porta il badge Modulo. Un esempio, lo Snippet hero:
<section class="hero hero--{{ variante|default:"hell" }}" id="{{ block.id }}">
{% if bild %}{{ image(bild.id, "100vw") }}{% endif %}
<h1>{{ titel }}</h1>
{{ text }}
{% if slots.buttons %}<div class="hero-actions">{% slot "buttons" %}{% endslot %}</div>{% endif %}
</section>
Con i parametri titel (testo, obbligatorio), bild (immagine), text (Markdown) e variante (scelta hell, dunkel, design) e lo slot buttons (ammesso: button, al massimo 3).
Nel modulo i parametri sono disponibili come variabili, già preparati:
| Tipo di parametro | Nel modulo |
|---|---|
| Testo, testo su più righe, URL, scelta, data, numero, sì/no | il valore, con escape come al solito |
| Markdown | HTML pronto – semplicemente {{ text }} |
| HTML | l’HTML invariato |
| Immagine, file | oggetto file come file(id) con url, srcset, width, height, alt, name … – ad es. {{ image(bild.id) }} o {{ bild.url }} |
| Collegamento, collegamenti | la voce collegata o l’elenco (per i visitatori solo quelle pubblicate) |
In più c’è block con block.id e block.type. Non esiste un parametro di tipo Blocchi – l’annidamento passa dagli slot. I nomi degli slot devono corrispondere a {% slot %} nello Snippet (default è lo slot predefinito); la scheda avvisa quando uno slot è solo nello schema o solo nel codice, e riprende con un clic gli slot dal codice.
Blocchi in una pagina
Nell’HTML della pagina decidi dove compaiono i blocchi:
{% include "header" %}
<main>
{% blocks page.blocks %}
</main>
{% include "footer" %}
Se il tag è nell’HTML o la pagina ha già dei blocchi, l’editor della pagina mostra la scheda Blocchi. Altrimenti il pulsante Blocchi nella barra degli strumenti della scheda HTML inserisce il tag (al cursore, altrimenti alla fine) e apre la scheda.
Come si lavora nell’editor a moduli:
- Aggiungi blocco apre la scelta per categoria, con ricerca. In uno slot vengono proposti solo i moduli lì ammessi.
- Un clic sulla barra del titolo apre il blocco: il modulo secondo lo schema, sotto gli slot con i loro blocchi. La barra del titolo mostra come riepilogo il primo testo compilato.
- Su/giù, duplica ed elimina con i pulsanti a destra. Trascinando la barra del titolo sposti i blocchi all’interno di un elenco e tra gli slot, purché il modulo vi sia ammesso.
- L’anteprima mostra lo stato non salvato. Mentre la scheda Blocchi è aperta, un clic su un blocco nell’anteprima lo apre nel modulo; il blocco scelto è contornato nell’anteprima.
Si salva insieme alla pagina (Salva, Ctrl+S) – con cronologia delle versioni e controllo dei conflitti come per l’HTML.
Collections con blocchi
Il tipo di campo Blocchi trasforma un campo di collection in un costruttore di pagine, per esempio il contenuto di un articolo del blog. Il modulo della voce mostra lo stesso editor; se la collection ha una pagina di dettaglio, anche qui un clic nell’anteprima sceglie il blocco. Il campo si mostra con lo stesso tag:
{% with post=item("blog", "slug", route.slug) %}
<h1>{{ post.titel }}</h1>
{% blocks post.inhalt %}
{% endwith %}
Nell’esportazione CSV l’albero dei blocchi è scritto come testo JSON; l’importazione CSV salta i valori non validi con un avviso.
Blocchi globali
Alcuni blocchi servono su molte pagine – un box newsletter, un teaser di contatto. Li crei in Snippets → Blocchi globali: nome, descrizione, cartella e un albero di blocchi nello stesso editor. Li inserisci con Aggiungi blocco → Blocchi globali; nella pagina resta solo un riferimento, e le modifiche al blocco globale valgono ovunque.
- Incorpora sostituisce il riferimento con una copia dei suoi blocchi. Da lì in poi li modifichi solo in quel punto.
- Accanto all’editor vedi dove è usato il blocco globale (pagine, voci, altri blocchi globali), con i link.
- L’eliminazione è possibile solo finché non è usato da nessuna parte.
- Un blocco globale non può contenere sé stesso, nemmeno indirettamente – lo rifiuta già il salvataggio.
- Negli slot che ammettono solo determinati moduli, i blocchi globali non vengono proposti.
- Adattare i parametri: sotto il riferimento puoi sovrascrivere singoli parametri dei blocchi contenuti nel blocco globale per questo solo utilizzo – ad esempio il titolo del riquadro newsletter solo in questa pagina. Apri Adatta i parametri di questo utilizzo, spunta adatta accanto al parametro e inserisci il nuovo valore; senza spunta vale il valore del blocco globale (e cambia con esso). Si possono adattare solo i parametri, mai struttura, ordine o slot. Se il blocco o il parametro viene rimosso in seguito dal blocco globale, l’adattamento viene ignorato.
Limiti e note
- Al salvataggio si controlla: modulo sconosciuto, parametri obbligatori mancanti, valori del tipo sbagliato, slot sconosciuti, moduli non ammessi o troppi blocchi in uno slot. Il messaggio indica la posizione nell’albero.
- Al massimo 10 livelli e 1 MB per albero di blocchi. I parametri che lo schema non conosce (più) vengono scartati al salvataggio.
- Un blocco il cui modulo è stato eliminato resta salvato ma sul sito live non mostra nulla; l’anteprima mostra un avviso. I campi vuoti non mostrano nulla.
- Immagini e file nei blocchi contano come usati per la Pulizia. Il CSS/JS collegato allo Snippet di un modulo viene caricato nelle pagine che usano quel blocco.
- I marcatori per la scelta nell’anteprima (
<!--ic-block:…-->) esistono solo nell’anteprima, mai sul sito live. - Per gli editor visuali (IllusionPublish) l’anteprima indica inoltre quale elemento mostra quale parametro (
data-ic-field). Funziona automaticamente quando un parametro testo, area di testo o Markdown sta da solo e invariato in un elemento – quindi<h2>{{ ueberschrift }}</h2>e<div class="text">{{ inhalt }}</div>invece di titolo e Markdown uno accanto all’altro nella stessa<section>; le immagini tramiteimage(bild.id)o{{ bild.url }}. Dove non va bene (ad es.{{ titel|upper }}), marca l’elemento con{{ block_field("titel") }}:<p{{ block_field("titel") }}>{{ titel|upper }}</p>. Sul sito live non esiste nessuno di questi marcatori. Dettagli nella documentazione API. - Per le integrazioni:
blocksnelle pagine, il tipo di campoblocks,GET /api/v1/blocks/typese/api/v1/global-blocks– dettagli nella documentazione API.
Cosa uso per cosa?
| Situazione | Strumento |
|---|---|
| Pagine che altri compongono senza HTML a partire da moduli pronti | Moduli + {% blocks page.blocks %} |
| Stesso header/footer su tutte le pagine | include (o un layout) |
| Struttura per molte pagine simili | extends + block |
| Blocco ripetuto con contenuto proprio (card, box, accordion, pulsante) | component + slot |
| Piccolo formattatore senza contenuto | Macro |
| Testo che poi evolve in modo indipendente | Snippet → Contenuto (copia) |