Eine Bildergalerie für RichTextEditor in Angular

Der Dialog „Insert Gallery“ im Angular-Beispiel, mit den Ordnern blog, products und team und vier Bildern vom Express-Server

Der Dialog „Insert Gallery“ von RichTextEditor braucht in Angular dieselben drei Dinge wie in jeder Single-Page-App: die Editor-Skripte, die vor der App geladen werden, eine Komponente, die den Editor zum richtigen Zeitpunkt erstellt und zerstört, und einen Dev-Server, der die Requests des Dialogs an Ihre API weiterleitet. Diese Seite zeigt, wie das in einer Standalone-Komponente in Angular 22 aussieht, getestet mit dem Express-Beispiel und ng serve.

Die Editor-Skripte in index.html laden

Der Editor besteht aus klassischen Browserskripten, die eine globale Variable RichTextEditor definieren. Fügen Sie sie in src/index.html ein, vor dem Angular-Bundle:

HTML · src/index.html
<!-- Plain browser scripts from public/, loaded before the Angular bundle. -->
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<script src="/rte-files.js"></script>

Die Dateien liegen in public/richtexteditor/. In einem neuen Angular-Projekt kopiert angular.json bereits alles aus public/ als statische Assets in den Build, dort ist also keine Änderung nötig. Das Beispiel behält den Editor als einzelne Dateien unter /richtexteditor/, so wie der Download sie ausliefert. Es verwendet nicht das Array "scripts" in angular.json, das Skripte zu einer Datei bündelt. Den Editor auf diese Weise haben wir nicht getestet.

Die Komponente

TypeScript · src/app/rich-text-editor.ts
// RichTextEditor is the global from the scripts in index.html.
declare const RichTextEditor: new (element: HTMLElement, options: object) => EditorInstance;
type EditorInstance = { setHTMLCode(html: string): void; getHTMLCode(): string; destroy(): void };

@Component({
  selector: 'app-rich-text-editor',
  template: `<div #container></div>`,
})
export class RichTextEditorComponent implements AfterViewInit, OnDestroy {
  readonly options = input.required<object>();
  readonly initialHtml = input('');
  private readonly container = viewChild.required<ElementRef<HTMLDivElement>>('container');
  private editor?: EditorInstance;

  ngAfterViewInit(): void {
    this.editor = new RichTextEditor(this.container().nativeElement, this.options());
    this.editor.setHTMLCode(this.initialHtml());
  }

  ngOnDestroy(): void {
    this.editor?.destroy();
    this.editor = undefined;
  }

  getHTML(): string {
    return this.editor?.getHTMLCode() ?? '';
  }
}
  • declare const RichTextEditor macht TypeScript die globale Variable bekannt, nur mit den drei Methoden, die die Komponente verwendet.
  • viewChild.required liefert das div aus dem Template. ngAfterViewInit ist der erste Hook, in dem es existiert, deshalb wird der Editor dort erstellt.
  • ngOnDestroy ruft destroy() auf, sodass der Editor mit der Komponente verschwindet, etwa bei einem Routenwechsel oder wenn ein @if false wird. Das Beispiel hat nur eine Seite, daher hat unsere Prüfung im Browser diesen Hook nicht durchlaufen.
  • Signal-Inputs (input.required, input) übergeben die Optionen und das Start-HTML. Sie werden einmal gelesen, beim Erstellen.

Das Projekt wurde mit ng new --minimal --zoneless erstellt. Der Editor funktionierte ohne zone.js: Er behandelt seine eigenen DOM-Ereignisse, und nichts in der Komponente ist auf Change Detection angewiesen. Die App übergibt die Optionen und liest das HTML über eine Template-Referenz:

TypeScript · src/app/app.ts
const options = {
  toolbar: 'full',
  galleryEndpoint: '/rte/gallery', // forwarded to Express by src/proxy.conf.json
  maxUploadFileSize: 4000000,
};

Die Schaltfläche „Show HTML“ des Beispiels ruft editor.getHTML() auf und gab <p>Hello from Angular.</p> aus.

/rte/ und /uploads/ während der Entwicklung weiterleiten

ng serve liefert die App auf Port 5113 aus. Die Proxy-Datei sendet die Routen für Galerie und Upload sowie die Bilder an das Express-Beispiel auf Port 5101:

JSON · src/proxy.conf.json
{
  "/rte/": { "target": "http://localhost:5101", "secure": false },
  "/uploads/": { "target": "http://localhost:5101", "secure": false }
}

Das Beispiel setzt sie einmal in angular.json, unter projects.frontend-angular.architect.serve.options, als "proxyConfig": "src/proxy.conf.json". Wir haben es auch als ng serve --proxy-config src/proxy.conf.json ausgeführt, mit demselben Ergebnis. Der Dev-Server von Angular basiert auf Vite. Angular wandelt Schlüssel mit Glob-Zeichen wie /api/** in reguläre Ausdrücke um und reicht einfache Schlüssel unverändert an Vite weiter. Vite vergleicht diese als Präfix, behalten Sie also den abschließenden Schrägstrich bei. Mit "/rte/" wurde /rte-files.js nicht weitergeleitet: ng serve lieferte die eigene Kopie der App aus public/ aus, ohne Header X-Powered-By: Express.

Der Proxy gilt nur für ng serve. Ein Produktions-Build hat keinen Proxy: Liefern Sie ihn und die API vom selben Origin aus.

Was der Dialog anzeigt

Das Bild oben zeigt den Dialog in dieser Angular-App mit den Ordnern und Bildern des Express-Servers, der in der Galerie-Anleitung für Node.js beschrieben ist. Wie in jedem Framework nennt der Hinweis der ausgelieferten Galerie JFIF, BMP und AVIF, die der Server ablehnt. Ein abgelehnter Upload zeigt die eigene Meldung des Dialogs „Upload failed for <name>.“

Testen

Laden Sie das Beispielprojekt zu dieser Anleitung und das Express-Beispiel herunter und starten Sie zuerst Express. Dann im Angular-Projekt:

Shell
npm install
npm start
  ➜  Local:   http://localhost:5113/

curl "http://localhost:5113/rte/gallery?folder="
{"currentFolder":"","currentFolderDisplay":"/","parentFolder":null,"folders":[...],"images":[...]}

npm start kopiert zuerst die Editor-Dateien nach public/. Setzen Sie RTE_DIR auf Ihren Editor-Ordner oder legen Sie den Ordner selbst unter public/richtexteditor ab. Öffnen Sie http://localhost:5113 und klicken Sie auf Insert Gallery. Wir haben den gesamten Ablauf in Chromium mit Angular 22.2 ausgeführt: Die Liste stimmte mit dem Server überein, New folder und Upload funktionierten über den Proxy, und Insert fügte das Bild in den Text ein.

Dieselbe Anleitung für andere Plattformen

React · Vue

Weiterlesen