HTML-Komponenten

Zuletzt aktualisiert 14 August 2026

Markup einer HTML-Komponente mit Flexie-Scripting-Tokens, das auf dem Server zu einem fertigen Bereich mit dem Namen des angemeldeten Kunden gerendert wird

Ziehen Sie eine HTML-Komponente auf die Seite und schreiben Sie in ihren Inhalt-Editor. Das ist die gesamte Autorenoberfläche: Markup, Styles und Skripte an einem Ort.

Es wird auf dem Server gerendert, für einen Kunden

Ihr Inhalt wird nicht so an den Browser geschickt, wie Sie ihn getippt haben. Er geht zuerst durch Flexie Scripting, mit dem angemeldeten Kunden im Kontext, und nur das Ergebnis geht hinüber:

<div class="welcome">
  <h2>Hallo {{ entity.first_name }}</h2>
  <p>Ihre Kundennummer ist {{ entity.id }}.</p>
</div>

Der Browser des Kunden empfängt Hallo Melissa. Die Vorlage erreicht ihn nie, es wird also nichts offengelegt, und es gibt nichts, worauf man warten müsste: sobald die Seite existiert, handelt sie bereits von jemandem.

Sie brauchen daher meist gar kein JavaScript. Der ganze Datensatz steht zur Verfügung: jedes Feld auf der Kundenentität, über seinen Alias. Das Dynamics-Menü des Editors fügt die Tokens für Sie ein, und die vollständige Sprachreferenz ist Flexie Scripting.

Führen Sie es aus, bevor Sie speichern

Der Editor hat einen Ausführen-Button. Er rendert, was Sie geschrieben haben (oder nur die Auswahl), und zeigt das Ergebnis in einem Bereich. So finden Sie einen Fehler in einem Token, bevor ein Kunde ihn findet.

Das ist hier wichtiger als anderswo: eine Vorlage mit einem Syntaxfehler wirft keine Exception. Ohne Prüfung erreicht der Fehler die Seite als ungerendertes {{ ... }}, und nichts sagt Ihnen das.

Styles: Ihre, und nur Ihre

Schreiben Sie einen <style>-Block, und er ist auf diese Komponente gekapselt. Ihre .card-Regel erreicht Ihre Karten und nichts sonst auf der Seite, zwei Komponenten können also beide .card, .panel und .grid verwenden, ohne voneinander zu wissen.

<style>
  :root {
    --accent: #0b84cf;
  }
  .card {
    border: 1px solid #d3dae5;
    border-radius: 8px;
    padding: 16px;
  }
</style>

Aus der Kapselung folgen zwei Regeln:

  • :root meint die eigene Wurzel dieser Komponente. Dorthin gehören benutzerdefinierte Eigenschaften, und das ist es, was Sie stylen, um der Komponente selbst einen Hintergrund, eine Schrift oder ein Padding zu geben.
  • Eine body { }-Regel trifft auf nichts zu. body liegt außerhalb der Komponente, das Reset, das die meisten oben in ein Stylesheet kopieren, ist hier also schlicht wirkungslos. Schreiben Sie stattdessen auf :root, was Sie gemeint haben.

Eine Komponente, die in einer anderen gezeichnet wird, erbt deren Styling für alles, was sie nicht selbst setzt. Das ist meist gewünscht; muss eine Komponente immun sein, setzen Sie die Eigenschaften, die ihr wichtig sind, statt sich auf die Grenze zu verlassen.

Gekapselte Styles brauchen einen aktuellen Browser (Chrome und Edge 118, Safari 17.4, Firefox 128 und neuer). Auf allem Älteren werden die Styles ignoriert und die Komponente wird ungestylt gezeichnet, was die richtige Art zu scheitern ist, denn die Alternative wäre, dass das Reset einer Komponente das ganze Portal umbaut.

Skripte: standardmäßig privat

Schreiben Sie einen <script>-Block, und er läuft, wenn die Komponente gezeichnet wird. Alles, was das Portal kann, liegt auf einem Objekt, FlexiePortal, das bereits da ist:

<script>
  FlexiePortal.onComponentLoaded('my-component', function (el, params) {
    // el     das Element, in das diese Komponente gezeichnet wurde
    // params warum sie geöffnet wurde, wenn sie ein Dialog ist. Sonst leer

    el.querySelector('.card').addEventListener('click', function () {
      FlexiePortal.openModal('detail')
    })
  })
</script>

'my-component' ist die eigene ID dieser Komponente, aus dem Einstellungsbereich.

Erledigen Sie Ihre Arbeit in diesem Handler und nicht am Anfang des Skripts. Drei Gründe, und jeder davon ist ein Fehler, in den Leute laufen:

  • er reicht Ihnen Ihr Element, Sie durchsuchen also nicht die ganze Seite nach Markup, das Ihnen ohnehin gehört;
  • er läuft erneut, wenn Ihre Komponente neu gezeichnet wird, und ein Dialog wird bei jedem Öffnen neu gezeichnet, Ihr Code bleibt also korrekt, statt nur einmal zu laufen;
  • er ist es, was Ihre Komponente auf der Arbeitsfläche des Builders funktionieren lässt, wo Sie sie beim Schreiben ansehen.

Alles Weitere, was das Portal bereitstellt, und jedes weitere Ereignis ist Die JavaScript-API.

Am Anfang Ihres Skripts gibt es keine Variablen el, api oder params. Alles, was das Portal anbietet, kommt von FlexiePortal, und Ihr Element erreicht Sie als Argument des Handlers oben.

Nichts, was Sie deklarieren, entkommt auf die Seite, var rows in einer Komponente kann also nicht mit var rows in einer anderen kollidieren. Mehrere <script>-Blöcke in einer Komponente laufen als ein Stück, der Reihe nach, und sehen die Variablen der jeweils anderen.

Ein Skript, das fehlschlägt, wird protokolliert, und das Markup wird trotzdem gezeichnet. Ein leerer Kasten sagt dem Leser weniger und Ihnen gar nichts, öffnen Sie also die Browser-Konsole, wenn etwas still nicht passiert.

Startet Ihre Komponente etwas, das ihr Markup überdauert, etwa einen Timer oder einen Listener auf window, beenden Sie es in FlexiePortal.onComponentUnloaded. Alles, was innerhalb Ihres eigenen Elements angehängt ist, braucht überhaupt kein Aufräumen.

Was nicht mitkommt

Vier Tags werden beim Speichern verworfen, und der Editor sagt, welche:

Verworfen Warum
<title>, <meta> sie benennen ein Dokument, das dies nicht ist
<base> es würde jeden Link im Portal umlenken
<link rel="stylesheet"> ein externes Stylesheet ist das Einzige, was sich nicht kapseln lässt
<script src="..."> es gibt nichts abzuräumen und nichts zu kapseln

Alles andere überlebt, auch ganze eingefügte Seiten: die Teile, auf die es ankommt, werden herausgenommen.

Zwei Arten, wie das Speichern Ihr Markup still verändert

Ihr Inhalt wird beim Speichern als HTML geparst, damit Styles und Skripte davon getrennt werden können. Dieser Parser hat Meinungen, und keine der beiden warnt Sie.

1. Nackter Text auf oberster Ebene wird zu einem Absatz

Anweisungen außerhalb jedes Elements sind Text, und loser Text wird in einen Absatz gepackt, dessen Außenabstand Ihre Komponente dann die Seite hinunterschiebt.

<!-- schiebt alles nach unten -->
{% set name = entity.first_name %}
<div class="panel"> ... </div>

<!-- richtig -->
<div class="panel">
  {% set name = entity.first_name %}
  ...
</div>

2. Ein Tag in einem Kommentar wird zu einem echten Tag

Ein Flexie-Scripting-Kommentar ({# ... #}) ist kein HTML-Kommentar. Der Parser weiß nicht, dass der Text auskommentiert ist, ein darin geschriebenes Tag wird also wirklich geöffnet und bleibt offen.

{# FALSCH: das öffnet einen echten Absatz #}
{# jede Zeile wird als <p> gezeichnet #}

{# RICHTIG #}
{# jede Zeile wird als Absatz gezeichnet #}

Beides ist unsichtbar, bis Sie sich ansehen, was tatsächlich gespeichert wurde, und das lohnt sich einmal nach dem Schreiben einer Komponente.

Der Rahmen und die Höhe

Zwei Einstellungen entscheiden, wie die Komponente auf der Seite sitzt.

Komponente rahmen (standardmäßig an) zeichnet sie im üblichen Bereich, mit ihrem Namen als Titel. Schalten Sie es aus, und es gibt gar keinen Bereich: der Kunde sieht exakt Ihr Markup, von Kante zu Kante. Ohne Bereich gibt es keine Titelleiste, das Namensfeld bleibt also erhalten, wird aber schreibgeschützt angezeigt. Den Rahmen wieder einzuschalten bedeutet daher kein erneutes Tippen.

Volle Breite, so hoch wie der Inhalt ist für ein Dokument, dessen Länge niemand kennen kann: eine Aufstellung, die für einen Kunden eine Seite und für einen anderen drei Seiten lang ist. Es nimmt die Komponente aus dem Raster, gibt ihr die volle Breite dort, wo Sie sie gezeichnet haben, und lässt sie auf das wachsen, was sie braucht. Alles darunter rückt genau um diesen Betrag nach unten.

Ohne das wird eine Komponente, die aus ihrem Rechteck wächst, abgeschnitten. Variiert Ihr Markup in der Länge, schalten Sie es ein.

Ändert sich, was Ihre Komponente zeichnet, während die Seite offen ist (eine Übersicht, die wächst, wenn der Kunde Abschnitte aufklappt), zeichnen Sie ihr Rechteck bescheiden. Die gezeichnete Höhe bleibt als Untergrenze erhalten, und eine großzügige hinterlässt unter dem kürzesten Zustand eine dauerhafte Lücke.

Dialoge

Zeichnen Sie keinen eigenen Dialog. Fügen Sie eine Dialog-Komponente hinzu, schreiben Sie ihr Markup auf dieselbe Weise, und öffnen Sie sie:

FlexiePortal.openModal('detail', { params: { orderId: 5002 } })

Sie bekommen einen im Fenster zentrierten Dialog mit der Kopfzeile des Produkts, Schließen-Button, Escape-Taste und Hintergrund, von denen Ihr Markup nichts besitzen oder am Laufen halten muss.

Im Dialog sagt ihm dasselbe Lade-Ereignis, um welchen Datensatz es geht:

FlexiePortal.onComponentLoaded('detail', function (el, params) {
  // params.orderId ist 5002
})

Ein Dialog wird bei jedem Öffnen neu aufgebaut, das feuert also einmal pro Öffnen, immer mit den Parametern dieses Öffnens.

Ein durchgerechnetes Beispiel

Ein kleiner Bereich, der den Kunden begrüßt und zeigt, wie vollständig seine Daten sind. Alles darin wird auf dem Server aufgelöst; es gibt nichts nachzuladen.

<style>
  :root {
    --ink: #1b2432;
    --muted: #55637a;
    --line: #d3dae5;
  }
  .panel {
    padding: 20px 22px;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: linear-gradient(100deg, #fff 0%, #eaf3fb 100%);
    color: var(--ink);
  }
  .panel h2 { margin: 0; font-size: 20px; }
  .panel p  { margin: 4px 0 0; color: var(--muted); font-size: 13px; }
  .meter    { height: 6px; border-radius: 999px; background: #dde5ee; }
  .meter i  { display: block; height: 100%; background: #0b84cf; }
</style>

<div class="panel">
  {% set filled = 0 %}
  {% if entity.first_name %}{% set filled = filled + 1 %}{% endif %}
  {% if entity.company %}{% set filled = filled + 1 %}{% endif %}
  {% if entity.city %}{% set filled = filled + 1 %}{% endif %}

  <h2>
    Willkommen zurück{% if entity.first_name %}, {{ entity.first_name }}{% endif %}
  </h2>
  <p>
    {% if entity.company %}{{ entity.company }}
    {% else %}Alles, was wir zu Ihnen haben, steht auf dieser Seite{% endif %}
  </p>

  <div class="meter"><i style="width: {{ (filled * 33) }}%"></i></div>
</div>

Beachten Sie, wo die Vorbereitung steht: innerhalb des Wurzelelements, aus dem oben genannten Grund.