Ajouter une galerie d’images à RichTextEditor avec Angular

La boîte de dialogue Insert Gallery de l’exemple Angular, qui liste les dossiers blog, products et team et quatre images du serveur Express

Dans Angular, la boîte de dialogue Insert Gallery de RichTextEditor a besoin des trois mêmes choses que dans n’importe quelle application monopage : les scripts de l’éditeur chargés avant l’application, un composant qui crée et détruit l’éditeur au bon moment, et un serveur de développement qui transmet les requêtes de la boîte de dialogue à votre API. Cette page montre à quoi cela ressemble dans un composant autonome Angular 22, testé avec l’exemple Express et ng serve.

Charger les scripts de l’éditeur dans index.html

L’éditeur est un ensemble de scripts navigateur classiques qui définissent une variable globale RichTextEditor. Placez-les dans src/index.html, avant le bundle Angular :

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>

Les fichiers se trouvent dans public/richtexteditor/. Dans un nouveau projet Angular, angular.json copie déjà tout le contenu de public/ dans le build en tant que ressources statiques : aucune modification n’est donc nécessaire de ce côté. L’exemple conserve l’éditeur sous forme de fichiers séparés sous /richtexteditor/, tels que les fournit le téléchargement. Il n’utilise pas le tableau "scripts" d'angular.json, qui regroupe les scripts en un seul fichier. Nous n’avons pas testé l’éditeur de cette façon.

Le composant

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 déclare la variable globale à TypeScript, avec uniquement les trois méthodes qu’utilise le composant.
  • viewChild.required fournit le div du template. ngAfterViewInit est le premier hook où il existe : c’est donc là que l’éditeur est créé.
  • ngOnDestroy appelle destroy(), si bien que l’éditeur disparaît avec le composant, par exemple lors d’un changement de route ou quand un @if devient faux. L’exemple n’a qu’une seule page : notre test dans le navigateur n’a donc pas sollicité ce hook.
  • Les signal inputs (input.required, input) transmettent les options et le HTML initial. Ils sont lus une seule fois, à la création.

Le projet a été créé avec ng new --minimal --zoneless. L’éditeur a fonctionné sans zone.js : il gère lui-même ses événements DOM, et rien dans le composant ne dépend de la détection des changements. L’application transmet les options et lit le HTML via une référence de template :

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

Le bouton Show HTML de l’exemple appelle editor.getHTML() et a affiché <p>Hello from Angular.</p>.

Transmettre /rte/ et /uploads/ pendant le développement

ng serve sert l’application sur le port 5113. Le fichier de proxy envoie les routes de la galerie et d’envoi, ainsi que les images, à l’exemple Express sur le port 5101 :

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

L’exemple le déclare une fois dans angular.json, sous projects.frontend-angular.architect.serve.options, avec "proxyConfig": "src/proxy.conf.json". Nous l’avons aussi lancé avec ng serve --proxy-config src/proxy.conf.json, avec le même résultat. Le serveur de développement d’Angular repose sur Vite. Angular convertit en expressions régulières les clés qui contiennent des caractères glob, comme /api/**, et transmet les clés simples telles quelles à Vite. Vite les compare par préfixe : conservez donc la barre oblique finale. Avec "/rte/", /rte-files.js n’a pas été transmis : ng serve a servi la copie propre à l’application depuis public/, sans en-tête X-Powered-By: Express.

Le proxy ne sert qu’à ng serve. Un build de production n’a pas de proxy : servez-le, ainsi que l’API, depuis une même origine.

Ce qu’affiche la boîte de dialogue

L’image en haut de la page montre la boîte de dialogue dans cette application Angular, avec les dossiers et les images du serveur Express décrit dans le guide sur la galerie en Node.js. Comme dans tous les frameworks, l’indication de la galerie dans la version publiée mentionne JFIF, BMP et AVIF, que le serveur refuse. Un envoi refusé affiche le message propre à la boîte de dialogue, « Upload failed for <name>. ».

Tester

Téléchargez le projet d’exemple de ce guide et l'exemple Express, puis démarrez d’abord Express. Ensuite, dans le projet Angular :

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

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

npm start copie d’abord les fichiers de l’éditeur dans public/. Définissez RTE_DIR sur votre dossier de l’éditeur, ou placez vous-même le dossier dans public/richtexteditor. Ouvrez http://localhost:5113 et cliquez sur Insert Gallery. Nous avons exécuté toute la séquence dans Chromium avec Angular 22.2 : la liste correspondait au serveur, New folder et Upload ont fonctionné à travers le proxy, et Insert a placé l’image dans le texte.

Le même guide pour d’autres plateformes

React · Vue

À lire ensuite