Brilliancy of quality
Dokumentation

Praktische Mirage-Template-Syntax zur Steuerung des Layouts

Sapphire I.C.D.S.

Grenze der Template-Ebene

Mirage ist für HTML- und Textdarstellung vorgesehen. Damit lassen sich Bereiche umordnen, Karten und Überschriften ändern, dekorative Container ergänzen, Klassen steuern sowie vorbereitete Listen und lokalisierte Beschriftungen ausgeben. Ein Template darf kein SQL ausführen, keine Rechte bestimmen, keine Daten ändern und keine Routenprüfung des Moduls ersetzen. Fehlt ein benötigtes Feld im Eingabekontext, kann eine Layoutänderung es nicht verlässlich erzeugen.

Öffentliche Templates liegen unter skins/<Layout>/<Skin>/<Module>/, administrative unter adminskins/... und Moderator-Templates unter moderatorskins/.... Ermitteln Sie vor der Änderung das aktive Layout und den aktiven Skin. Derselbe Dateiname kann in mehreren Bäumen vorkommen; die Änderung einer inaktiven Kopie wirkt sich nicht auf die Seite aus.

Escaped Output

Die primäre und sicherste Ausgabeform verwendet doppelte geschweifte Klammern:

<h1>{{ Context.Table("page").Data("title") }}</h1>
<p>{{ Context.Table("page").Data("summary") }}</p>

Der Text wird für HTML maskiert, sodass Zeichen aus den Daten nicht zu Tags oder Attributen werden. Verwenden Sie diese Form für Titel, Namen, URLs in Attributen, Beschriftungen, Suchtexte und alle Werte, die ein Benutzer eingegeben haben könnte.

Raw Output wird mit einem Ausrufezeichen markiert:

{{! Former.BuildHTML(Context.Table("form").Data("submit_ir")) }}

Verwenden Sie ihn nur für Quellen, die bereits einen sicheren HTML-Vertrag besitzen, etwa ein von Former erzeugtes Steuerelement, eine Echelon Zone oder vorserialisiertes JSON-LD. Wechseln Sie nicht zu Raw Output, nur damit ein Tag aus einem Benutzerfeld funktioniert. Dadurch würden Daten zu aktiver Auszeichnung.

Benannte Context-Tabellen

Verwenden Sie für neue Templates Context.Table. Eine Seite kann meta, page, assets und Modultabellen mit dem Präfix __ erhalten. Die wichtigsten Aufrufe sind:

  • Context.HasTable("items") prüft das Vorhandensein einer Tabelle;
  • Context.Table("items").Data("title") liest ein Feld der aktuellen Zeile;
  • RowCount() liefert die Zeilenanzahl;
  • Rewind() setzt den Cursor zurück;
  • NextRow() wechselt zur nächsten Zeile.

Die kompatible Syntax Table.Data(...) bleibt in älteren Einzeltabellen-Templates erhalten, sollte aber nicht ohne Grund mit benanntem Kontext gemischt werden.

Bedingungen, Werte und Kommentare

{% if Context.Table("page").Data("status") == "published" %}
  <span class="badge badge--live">{{ Boardwords("status_published") | default("Veröffentlicht") }}</span>
{% elif Context.Table("page").Data("status") == "draft" %}
  <span class="badge badge--draft">{{ Boardwords("status_draft") | default("Entwurf") }}</span>
{% else %}
  <span class="badge">{{ Boardwords("status_unknown") | default("Unbekannt") }}</span>
{% endif %}

Flags werden häufig als Strings "1" und "0" geliefert und sollten ausdrücklich so verglichen werden. Eine Variable wird mit {% set name = value %} gesetzt. Ein Kommentar hat die Form {# Kommentar #} und erscheint nicht im HTML.

Für Ersatzwerte stehen zwei Formen zur Verfügung:

{{ Context.Table("meta").Data("title") ?? "Ohne Titel" }}
{{ Boardwords("catalog_title") | default("Katalog") }}

Ein ternärer Ausdruck eignet sich für einen kurzen Klassenwert; größere Bereiche werden mit einem normalen if formuliert:

{{ Context.Table("page").Data("compact") == "1" ? "card--compact" : "card--wide" }}

Sichere Schleife über Zeilen

Eine benannte Tabelle wird nicht automatisch zu einem Array von Zeilenobjekten. Verwenden Sie das vollständige Cursormuster:

{% if Context.Table("products").RowCount() > 0 %}
  {% set __ = Context.Table("products").Rewind() %}
  <div class="product-grid">
    {% for _ in range(0, Context.Table("products").RowCount(), 1) %}
      {% if Context.Table("products").NextRow() %}
        <article class="product-card{% if loop.even %} product-card--even{% endif %}">
          <h3>{{ Context.Table("products").Data("title") }}</h3>
          <p>{{ Context.Table("products").Data("summary") }}</p>
        </article>
      {% endif %}
    {% endfor %}
  </div>
{% else %}
  <p>{{ Boardwords("products_empty") | default("Noch keine Einträge") }}</p>
{% endif %}

Die Reihenfolge ist verbindlich: Zeilen prüfen, Rewind() aufrufen, den Bereich durchlaufen und vor dem Lesen von Data() jeweils NextRow() aufrufen. In der Schleife stehen loop.index, loop.first, loop.last, loop.odd und loop.even bereit.

Lokalisierung, Formulare und Zonen

Statische Benutzerbeschriftungen werden über Boardwords mit verständlichem Fallback ausgegeben. Dadurch bleibt das Design beim Kopieren zwischen Skin-Bäumen übersetzbar. Wird ein Formularfeld als *_ir geliefert, bauen Sie das Input-Element nicht manuell:

<label>{{ Boardwords("profile_name") | default("Profilname") }}</label>
{{! Former.BuildHTML(Context.Table("profile").Data("name_ir"), "class", "profile-form__input") }}

Mirage steuert Container, Raster und Klasse; Former besitzt den Vertrag des HTML-Steuerelements. Auch Inhalt einer Echelon Zone wird nur deshalb raw ausgegeben, weil die Zone ein vertrauenswürdiger Fragmentproduzent ist. Ein beliebiges Tabellenfeld besitzt diesen Status nicht.

Änderung prüfen

  1. Nehmen Sie eine enge Änderung im aktiven Template vor, ohne den gesamten Shell zu kopieren.
  2. Prüfen Sie die Seite mit Daten, leerer Liste und fehlenden optionalen Feldern.
  3. Testen Sie Sonderzeichen in Texten und Attributen.
  4. Prüfen Sie alle verfügbaren Sprachen und Boardwords-Fallbacks.
  5. Testen Sie Desktop und Mobilansicht sowie Validierungsfehler administrativer Formulare.
  6. Stellen Sie sicher, dass Raw Output nur für vertrauenswürdige HTML-Produzenten verwendet wird.

Benötigt das Template ein neues Recht, SQL oder eine Geschäftsregel, ist die Mirage-Grenze überschritten. Die korrekte Erweiterung bereitet zuerst Daten im Modul vor; das Template bleibt eine vorhersagbare Darstellungsschicht.

Seitenkomposition und dynamische Fragmente

Bearbeiten Sie gemeinsamen Shell, Navigation und Inhalt des Anwendungsmoduls getrennt. Verschieben Sie nicht den gesamten Seitenrahmen in ein Karten-Template; das erzeugt Duplikate und beeinträchtigt den Skin-Wechsel. Verwenden Sie für Fragmente, die durch Filter, Pager oder Tabs aktualisiert werden, den etablierten AJAX-Contour mit Mirage. Kanonische öffentliche Inhalte, die beim ersten Rendern und für SEO relevant sind, bleiben im normalen Seitenrendering.

Verwendet ein Template einen zusätzlichen Namespace eines Callback-Pakets, etwa zur Datumsformatierung, muss dieses Paket im Ziel-Runtime vorhanden sein. Ein solcher Callback ist eine Abhängigkeit des Templates. Nutzen Sie, wo das Design es erlaubt, einen verständlichen Fallback und imitieren Sie einen fehlenden Callback nicht durch komplexe String-Ausdrücke.

Sichere Review-Praxis

Vergleichen Sie das resultierende HTML vor und nach der Änderung, nicht nur den .mirage-Quelltext. Prüfen Sie ausgeglichene Tags, eindeutige IDs, zugängliche Labels, gültige Links und das Fehlen verschachtelter interaktiver Elemente. Testen Sie Listen mit einer und mehreren Zeilen, da Cursorfehler bei einem einzigen Datensatz unsichtbar bleiben können. Prüfen Sie beim Übertragen zwischen Skin-Bäumen Tabellen- und Feldnamen erneut; optisch ähnliche Module müssen nicht denselben Kontext erhalten.