Eine Bildergalerie für RichTextEditor in Angular

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:
<!-- 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
// 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 RichTextEditormacht TypeScript die globale Variable bekannt, nur mit den drei Methoden, die die Komponente verwendet.viewChild.requiredliefert dasdivaus dem Template.ngAfterViewInitist der erste Hook, in dem es existiert, deshalb wird der Editor dort erstellt.ngOnDestroyruftdestroy()auf, sodass der Editor mit der Komponente verschwindet, etwa bei einem Routenwechsel oder wenn ein@iffalse 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:
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:
{
"/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:
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.