Skip to content

Bilder

Wenn ein Benutzer einen Bildblock hinzufügt, zeigt der Editor ein Textfeld an, in das er eine Bild-URL einfügen kann. Die Symbolleiste des Bildblocks stellt Felder für src, alt, width, align und eine optionale linkUrl bereit.

Felder des Bildblocks

Benutzerdefinierter Medien-Picker

Wenn der Callback onRequestMedia bereitgestellt wird, erscheint eine Durchsuchen-Schaltfläche neben der URL-Eingabe.

Schaltfläche des Medien-Pickers

Der Editor ruft diese Funktion auf, wenn der Benutzer auf die Schaltfläche klickt. Geben Sie ein MediaResult-Objekt zurück oder null, wenn der Benutzer abbricht. Wenn alt angegeben ist, füllt der Editor automatisch den Alternativtext des Bildes aus.

ts
import { init } from '@templatical/editor';

const editor = await init({
  container: '#editor',
  async onRequestMedia() {
    // Öffnen Sie Ihr eigenes Modal, Ihren Datei-Browser oder Asset-Manager
    const image = await openMyMediaModal();
    if (!image) return null;

    return { url: image.url, alt: image.alt };
  },
});

Die Typsignatur:

ts
interface MediaResult {
  url: string;
  alt?: string;
}

interface MediaRequestContext {
  /** Medienkategorien, die der Editor anfordert (z. B. `['images']`). */
  accept?: MediaCategory[];
  /**
   * Dateien, die direkt auf einen Bildblock oder ein Bildfeld gezogen wurden.
   * Nur bei Drag-and-Drop-Anfragen vorhanden; nicht, wenn der Benutzer auf
   * „Medien durchsuchen“ klickt.
   */
  files?: File[];
}

type OnRequestMedia = (context?: MediaRequestContext) => Promise<MediaResult | null>;

Per Drag-and-Drop hochladen

Benutzer können eine Bilddatei von ihrem Computer direkt auf einen Bildblock (leer oder gefüllt), das Bildfeld in der Seitenleiste oder das Bildfeld eines benutzerdefinierten Blocks ziehen. Dabei ruft der Editor denselben onRequestMedia-Handler auf — jedoch mit der abgelegten Datei in context.files:

ts
const editor = await init({
  container: '#editor',
  async onRequestMedia(context) {
    // Eine Datei wurde abgelegt — laden Sie sie hoch und geben Sie die URL zurück.
    if (context?.files?.length) {
      const url = await uploadToMyBackend(context.files[0]);
      return { url };
    }

    // Keine Datei — der Benutzer hat auf „Medien durchsuchen“ geklickt.
    const image = await openMyMediaModal();
    return image ? { url: image.url, alt: image.alt } : null;
  },
});

Der Editor lädt selbst nichts hoch — er übergibt Ihnen die File und verwendet die zurückgegebene URL, genau wie beim „Medien durchsuchen“-Pfad. Einige Hinweise:

  • Eine Datei pro Drop. files ist aus Gründen der Vorwärtskompatibilität ein Array, aber der Editor sendet derzeit eine einzelne Datei (files[0]).
  • Nur Bilder. Der Editor filtert abgelegte Dateien vor dem Aufruf auf Bild-MIME-Typen.
  • Kein Handler, kein Drop. Ist onRequestMedia nicht gesetzt, erscheint kein Drop-Hinweis und Drops werden ignoriert.
  • Keine blob:-URL zurückgeben. URL.createObjectURL(file) ist sitzungslokal und bricht den Export. Laden Sie die Datei hoch und geben Sie eine dauerhafte URL (oder eine data:-URL) zurück.

Bei Cloud-Editoren werden abgelegte Dateien automatisch in Ihre Templatical-Medienbibliothek hochgeladen — kein onRequestMedia nötig (ein eigener Handler hat weiterhin Vorrang).

Reine Anzeige-Auflösung von Bild-URLs

Manche Integrationen speichern kanonische Bildreferenzen, die nicht direkt anzeigbar sind — zum Beispiel eine offlinefähige Anwendung, deren Vorlagen Bilder über einen einfachen Dateinamen (logo.png) referenzieren, anzeigbar nur über kurzlebige blob:-URLs aus dem lokalen Speicher. onRequestMedia deckt den Medien-Browser-Pfad ab; tippt oder fügt ein Benutzer einen solchen Wert jedoch direkt in das src-Feld ein, kann die Arbeitsfläche nichts anzeigen.

Der Callback resolveImageUrl schließt diese Lücke. Er bildet einen src-Wert auf eine Vorschau-URL nur für die Arbeitsfläche ab — das Inhaltsmodell behält den kanonischen Wert, und toMjml() exportiert ihn unverändert:

ts
const editor = await init({
  container: '#editor',
  async resolveImageUrl(src) {
    const file = await myFileStore.lookup(src);
    return file ? URL.createObjectURL(file) : null;
  },
});

Die Typsignatur:

ts
resolveImageUrl?: (src: string) => string | null | Promise<string | null>;

Geben Sie die Vorschau-URL zurück, oder null (bzw. den Eingabewert), um den src unverändert zu verwenden.

So ruft der Editor den Callback auf:

  • Einmal pro bestätigtem Wert. Eingaben im src-Feld werden entprellt, sodass Teilwerte (lo, logo.p, …) Ihren Resolver nie erreichen.
  • Pro src zwischengespeichert für die Lebensdauer der Editor-Instanz — derselbe src in mehreren Blöcken wird nur einmal aufgelöst.
  • Fehler werden abgefangen. Ein geworfener Fehler oder eine abgelehnte Promise wird als „unverändert verwenden" zwischengespeichert; der Editor wiederholt den Aufruf für denselben src nicht. Das gilt auch für vorübergehende Fehler: Ein src, dessen Auflösung fehlgeschlagen ist, bleibt unaufgelöst, bis der Editor neu initialisiert wird. Ein Hook zum erneuten Auslösen der Auflösung ist für eine zukünftige Version angedacht.
  • Merge-Tag-srcs werden übersprungen. Ein src wie {{product.image}} wird nie an den Resolver übergeben; stattdessen wird die placeholderUrl (falls gesetzt) aufgelöst.
  • Video-Thumbnails sind ebenfalls abgedeckt. Eine explizite Video-thumbnailUrl (und die placeholderUrl eines Videoblocks) wird auf dieselbe Weise aufgelöst. Automatisch aus einer YouTube-/Vimeo-URL abgeleitete Thumbnails sind bereits echte URLs und werden nie an den Resolver übergeben.
  • Bewusst nur für die Anzeige. Anders als eine blob:-URL aus onRequestMedia (die im Export landen würde — siehe oben) gelangt eine URL aus resolveImageUrl nie in den Vorlageninhalt.

Eigenschaften des Bildblocks

Der Typ ImageBlock definiert alle konfigurierbaren Eigenschaften:

EigenschaftTypBeschreibung
srcstringQuell-URL des Bildes
altstringAlternativtext für Barrierefreiheit
widthnumber | 'full'Bildbreite in Pixeln oder 'full' für 100%
heightnumber (optional)Bildhöhe in Pixeln. Ohne Angabe wird sie aus der Breite abgeleitet
align'left' | 'center' | 'right'Horizontale Ausrichtung
borderRadiusnumber (optional)Eckenradius in Pixeln. Ohne Angabe oder 0 bleiben die Ecken eckig
decorativeboolean (optional)Blendet das Bild für Screenreader aus und sendet ein leeres alt
linkUrlstring (optional)Umschließt das Bild mit einem Link
linkOpenInNewTabboolean (optional)Öffnet den Link in einem neuen Tab
placeholderUrlstring (optional)Vorschaubild zur Entwurfszeit, wenn src ein Merge-Tag verwendet

Höhe

height ist optional, und es weglassen ist meist richtig: Die Höhe wird dann aus der Breite abgeleitet und das Bild behält sein Seitenverhältnis. Setzen Sie sie, wenn das Layout eine feste Box braucht -- einen Banner-Platz mit bekannter Größe oder eine Reihe von Bildern, die auf einer Linie liegen müssen.

Werden width und height gemeinsam gesetzt, wird das Bild auf diese Box gestreckt. Es wird nicht beschnitten, denn object-fit wird von Outlook und den meisten E-Mail-Clients nicht unterstützt -- die Editor-Leinwand streckt deshalb genauso, statt einen Beschnitt zu versprechen, den der Posteingang nicht liefert. Passen Sie das Verhältnis an das Quellbild an oder skalieren Sie das Asset vor dem Upload.

Eckenradius

borderRadius rundet die Ecken des Bildes ab, in Pixeln. Ohne Angabe oder bei 0 bleiben die Ecken eckig -- das gilt für jeden bestehenden Block.

Für ein kreisrundes Avatar- oder Portraitbild braucht der Block ein quadratisches Quellbild und einen Radius von mindestens der Hälfte seiner gerenderten Größe: Ein 240px-Quadrat benötigt 120, jeder größere Wert (999 ist die übliche Kurzform) ergibt denselben Kreis. Ein nicht-quadratisches Bild wird zur Ellipse, da es keinen Beschnitt gibt -- siehe Höhe oben.

Die Unterstützung ist gut, aber nicht vollständig: Apple Mail, iOS Mail, Gmail und Outlook.com setzen es um, Outlook unter Windows ignoriert es und zeigt eckige Ecken. Behandeln Sie es als Progressive Enhancement, nicht als Grundlage eines Layouts.

Placeholder-URL

Wenn das src-Feld ein Merge-Tag enthält (z. B. {{product.image}}), wird das eigentliche Bild im Editor nicht gerendert. Verwenden Sie placeholderUrl, um ein Platzhalterbild im Editor bereitzustellen. Dieser Wert ist in der exportierten Ausgabe nicht enthalten.

Best Practices

  • Verwenden Sie absolute URLs -- Relative Pfade werden in E-Mail-Clients nicht aufgelöst. Verwenden Sie immer https://-URLs.
  • Bevorzugen Sie PNG oder JPG -- SVG und WebP haben begrenzte Unterstützung in E-Mail-Clients. Verwenden Sie PNG für Grafiken mit Transparenz und JPG für Fotos.
  • Halten Sie Dateigrößen angemessen -- Große Bilder verlangsamen das Laden für die Empfänger.
  • Legen Sie immer Alternativtext fest -- Viele E-Mail-Clients (insbesondere Outlook) blockieren Bilder standardmäßig. Empfänger sehen den Alternativtext, bis sie sich entscheiden, Bilder zu laden.
  • Explizite Breite festlegen -- E-Mail-Clients rendern Bilder möglicherweise in ihrer Originalgröße, wenn keine Breite angegeben ist, wodurch Ihr Layout auf kleinen Bildschirmen zerstört wird.
  • Höhe nur setzen, wenn sie gebraucht wird -- Die Breite allein erhält das Seitenverhältnis. Eine Höhe, die nicht zum Verhältnis der Quelle passt, streckt das Bild in jedem Client.