PageKit — der selbst gehostete GrapesJS-Website-Builder, als Quellcode. Early Access sichern

GrapesJS Angular-Integration

GrapesJS Angular:Baue einen Visual Editor mit Angular

Integrieren Sie den Open-Source-Visual Editor GrapesJS in Angular-Anwendungen, um Drag-and-Drop-Seiteneditoren, CMS-Editoren, Landingpage-Builder, E-Mail-Editoren und White-Label-Visual-Editing-Erlebnisse zu erstellen.

Open SourceSelbstgehostetHTML- und CSS-AusgangErweiterbarAngular-kompatibel

26k+

GitHub-Sterne

1.4M+

npm-Downloads / Monat

100+

Plugins auf GJS.Market

$0

Lizenzgebühr

Live-Editor

Versuche GrapesJS in einer Angular-Anwendung

In einem neuen Tab öffnen

Der Standard-GrapesJS-Build – der Editor, den du von 'GrapesJS.init()' mit dem Standard-Preset bekommst, bevor ein Plugin hinzugefügt wird. Ziehe einen Block aus dem rechten Panel, wähle ihn aus und bearbeite seine Typografie sowie Abstände im Style-Manager.

grapesjs.com/demo.htmlKostenlos

Lädt eine Drittanbieter-Seite von grapesjs.com. Es wird nichts angefordert, bis man klickt.

Das sind Live-GrapesJS-Builds, keine Angular-Demos – und genau das ist der Punkt. GrapesJS rendert in ein Container-Element, das es vollständig besitzt, sodass die Canvas, Blöcke, Style-Manager, responsive Previews, Asset Picker und Undo-History, die du unten anführst, sich identisch verhalten, sobald eine Angular-Komponente diesen Container hostet.

  1. Angular application
  2. GrapesJS editor
  3. HTML + CSS + project data
  4. Your backend / CMS / SaaS

Angular treibt deine Anwendung an. GrapesJS ermöglicht das visuelle Bearbeitungserlebnis. Es kommt heraus, dass HTML, CSS und ein Projekt-JSON-Dokument entstehen, das dein eigenes Backend speichert.

Direkte Antwort

Kann man GrapesJS mit Angular verwenden?

Ja

GrapesJS ist framework-agnostisch und kann in Angular-Anwendungen integriert werden. Du initialisierst den Editor über Angular-Lebenszyklus-Hooks, behältst die Editor-Instanz unter deiner Kontrolle, verbindest GrapesJS-Ereignisse mit der Angular-Anwendungslogik und speicherst Projektdaten über dein eigenes Backend.

  • GrapesJS ist eine einfache JavaScript-Bibliothek ohne Framework-Bindung – sie benötigt ein DOM-Element und sonst nichts.
  • Initialisiere es in ngAfterViewInit, sobald das Container-Element tatsächlich im DOM existiert.
  • Zerstöre es in ngOnDestroy, damit Hörer, DOM-Knoten und der Undo-Stack zusammen mit der Komponente freigegeben werden.
  • Behalte die Editor-Instanz in einem Service, falls mehr als eine Komponente mit ihr kommunizieren muss.
  • Editor-Events sind gewöhnliche Emitter-Events – überbrücken Sie sie in Signale, Observables oder NgZone, je nach Bedarf Ihrer App.
  • Speicher und Assets gehören dir: GrapesJS ruft deine Endpunkte auf, es liefert kein Backend.

Es gibt keinen offiziellen Angular-Wrapper. Das GrapesJS-Projekt unterhält einen React-Wrapper und kein Angular-Äquivalent, sodass eine Angular-Integration bedeutet, eine Komponente zu schreiben – ungefähr die dreißig Zeilen im untenstehenden Beispiel – oder ein Drittanbieter-Paket nach seinen eigenen Leistungen zu bewerten.

Springen wir zum Arbeitsbeispiel
Die Arbeitsteilung

Was ist GrapesJS für Angular?

GrapesJS ist die visuelle Bearbeitungs-Engine. Angular ist das Anwendungsframework. Fast jedes Integrationsproblem, auf das Leute stoßen, entstehen daher, dass man erwartet, dass einer von ihnen die Arbeit des anderen erledigt, daher lohnt es sich, die Grenze zwischen ihnen explizit zu benennen.

Angular

Angular besitzt die Anwendung

Alles rund um die Canvas: wer der Nutzer ist, was er tun darf und wohin das Ergebnis führt.

  • Routing
  • Authentifizierung
  • Anwendungs-UI
  • Nutzer
  • Berechtigungen
  • Abrechnung
  • API-Schicht
  • Anwendungszustand
GrapesJS

GrapesJS besitzt die Bearbeitungsfläche

Alles innerhalb der Canvas: was gezogen, ausgewählt, gestaltet und exportiert werden kann.

  • Canvas
  • Komponenten
  • Blöcke
  • Style Manager
  • Asset Manager
  • Befehle
  • Speichermanager
  • Gerätemanager

Angular ist deine Anwendung. GrapesJS ist deine visuelle Bearbeitungsmaschine.

Aufbau

GrapesJS in Angular installieren

Eine Abhängigkeit und ein Stylesheet. GrapesJS liefert sein eigenes CSS aus, und ohne es mountet der Editor ein, rendert aber als unstyletes Markup – was der häufigste Grund ist, warum eine erste Integration kaputt aussieht.

1 — Addiere die Abhängigkeit

npm install grapesjs

2 — Stylesheet registrieren

angular.jsonjson
// angular.json → projects.<app>.architect.build.options
{
  "styles": [
    "src/styles.css",
    "node_modules/grapesjs/dist/css/grapes.min.css"
  ]
}

Das Registrieren des Stylesheets in angular.json wendet es global an. Wenn der Editor auf einer faul geladenen Route lebt und du nicht überall für CSS bezahlen möchtest, importiere es stattdessen im eigenen Stylesheet der Editor-Komponente.

Warum ngAfterViewInit?

GrapesJS benötigt ein echtes Element zum Anhängen und misst dieses Element beim Initialisieren. In ngOnInit wurde die Vorlage noch nicht gerendert, daher ist eine @ViewChild-Referenz noch undefiniert und der Aufruf schlägt komplett fehl. ngAfterViewInit läuft, nachdem Angular die Ansicht der Komponente erstellt hat, was der erste Moment ist, in dem der Container existiert. Die gleiche Begründung erklärt die Höhenregel im untenstehenden Stylesheet: Ein Element ohne aufgelöste Höhe erzeugt einen Editor, der fehlerfrei initialisiert und nichts anzeigt.

Bevor du die Komponente schreibst

Der Behälter gehört dir nicht

GrapesJS ersetzt den Inhalt des Elements, das du ihm übergibst. Rendere Angular-Template-Inhalte nicht innerhalb dieses Elements und binde dich nicht an etwas darunter.

Gib ihm eine Höhe

Das Host-Element benötigt eine festgelegte Höhe vor der Initialisierung. Ein Container mit null Höhe montiert einen Editor, den du nicht sehen kannst.

Eine Instanz pro Komponente

Behalte einen einzigen Verweis auf den Editor und zerstöre ihn in ngOnDestroy. Zwei Live-Instanzen auf einer Route kämpfen um Tastenkombinationen.

Typen sind enthalten

Das grapesjs-Paket enthält eigene TypeScript-Definitionen, sodass Editor, Component und ProjectData ohne separates @types-Paket importierbar sind.

Minimales Arbeitsbeispiel

Eine vollständige Angular-Editor-Komponente

Drei Dateien, nichts generiert, nichts versteckt. Kopieren Sie sie in eine Angular-Anwendung und Sie haben einen funktionierenden visuellen Editor auf einer Route. Alles später auf dieser Seite – Speicher, Assets, benutzerdefinierte Komponenten, Plugins – ist eine Ergänzung zu dieser Komponente, keine Neuschreibung davon.

editor.component.tsts
// editor.component.ts
import {
  AfterViewInit,
  Component,
  ElementRef,
  OnDestroy,
  ViewChild,
} from '@angular/core';
import grapesjs, { type Editor } from 'grapesjs';

@Component({
  selector: 'app-editor',
  templateUrl: './editor.component.html',
  styleUrl: './editor.component.css',
})
export class EditorComponent implements AfterViewInit, OnDestroy {
  // Resolved by the time ngAfterViewInit runs — that is the whole reason
  // initialisation lives there and not in ngOnInit.
  @ViewChild('editorHost') private host!: ElementRef<HTMLDivElement>;

  private editor?: Editor;

  ngAfterViewInit(): void {
    this.editor = grapesjs.init({
      container: this.host.nativeElement,
      height: '100%',
      width: 'auto',
      fromElement: false,
      // No persistence yet. Wire your own API before shipping — see Storage.
      storageManager: false,
      blockManager: {
        blocks: [
          {
            id: 'section',
            label: 'Section',
            category: 'Layout',
            content: '<section class="section"><h2>Headline</h2></section>',
          },
          {
            id: 'text',
            label: 'Text',
            category: 'Basic',
            content: { type: 'text', content: 'Edit me' },
          },
          { id: 'image', label: 'Image', category: 'Basic', content: { type: 'image' } },
        ],
      },
    });
  }

  ngOnDestroy(): void {
    // GrapesJS owns DOM nodes, document listeners and an undo stack. Angular
    // removes the host element; only destroy() releases the rest.
    this.editor?.destroy();
    this.editor = undefined;
  }
}
editor.component.htmlhtml
<!-- editor.component.html -->
<!-- GrapesJS takes this element over completely. Do not render Angular
     content inside it — the editor owns everything below #editorHost. -->
<div class="editor-shell">
  <div #editorHost class="editor-host"></div>
</div>
editor.component.csscss
/* editor.component.css */
.editor-shell {
  display: block;
  block-size: 100dvh;
}

/* GrapesJS measures its container at init. A host with no resolved height
   produces an editor that mounts successfully and renders nothing. */
.editor-host {
  block-size: 100%;
}

Das ist die gesamte Integration. Die Teile, die es wert sind, noch einmal durchzulesen, sind die beiden Lebenszyklus-Hooks: die Initialisierung in ngAfterViewInit, weil der Container zuerst existieren muss, und destroy() in ngOnDestroy, weil das Entfernen des Hostelements durch Angular nicht das an das Dokument angehängte GrapesJS freigibt.

Lade den Editor auf seiner eigenen Route

Der Editor ist das schwerste in den meisten Anwendungen, die einen enthalten. Wenn man ihn hinter eine faul geladene Route legt, bleibt er für jeden Nutzer, der ihn nie öffnet, aus dem ursprünglichen Bundle heraus.

app.routes.tsts
// app.routes.ts — the editor is the heaviest route in the app. Load it last.
import type { Routes } from '@angular/router';

export const routes: Routes = [
  { path: '', loadComponent: () => import('./home/home.component').then((m) => m.HomeComponent) },
  {
    path: 'pages/:id/edit',
    loadComponent: () => import('./editor/editor.component').then((m) => m.EditorComponent),
  },
];
Lebenszyklus

Integration von GrapesJS in den Angular-Lebenszyklus

Die Lebensdauer des Editors muss an die der Komponenten gebunden sein. Vier Teile übernehmen diese Arbeit, und jedes von ihnen versagt auf erkennbare Weise, wenn es fehlt.

@ViewChild

Erhält eine Referenz auf das Container-Element. Abfrage anhand der Vorlagenreferenzvariable, nicht über den CSS-Selektor, damit ein Refactor des Markups die Suche nicht stillschweigend unterbrechen kann.

ngAfterViewInit

Der erste Hook, der nach der Ansicht der Komponente läuft, existiert. Initialisieren Sie hier. Das frühere Initialisieren ergibt eine undefinierte Elementreferenz.

Die Editor-Instanz

Halte es in einem privaten Feld oder in einem Service, falls eine Toolbar-Komponente anderswo Befehle auslösen muss. Alles, was du danach machst, läuft über dieses eine Objekt.

ngOnDestroy

Ruf editor.destroy() auf. Es entfernt das DOM des Editors, trennt die Dokumenten-Listener und verwirft den Rückgängig-Stapel. Überspringt man es, entsteht ein Leck, das die Navigation übersteht.

Initialisieren Sie GrapesJS nicht, bevor der Editor-Container existiert.

Dies ist der Fehler, der die meisten "GrapesJS funktioniert nicht in Angular"-Berichte ausmacht. In ngOnInit und in jedem Konstruktor wurde die Ansicht nicht erstellt – die Containerreferenz ist undefiniert und die Initialisierung wird geworfen. Wenn sich der Container in einem @if-Block oder einer deferred View befindet, reicht ngAfterViewInit ebenfalls nicht aus: Warte, bis dieser Branch tatsächlich gerendert ist, bevor du initialisierst.

Veränderungserkennung

GrapesJS- und Angular-Veränderungserkennung

GrapesJS verbindet eine große Anzahl von Zuhörern mit seiner Canvas und feuert sie kontinuierlich, während eine Komponente gezogen oder verkleinert wird. In einer zonenbasierten Angular-Anwendung plant jeder dieser Zuhörer einen Änderungserkennungsdurchlauf, obwohl sich in Ihrem Komponentenbaum nichts geändert hat.

editor.component.ts (Auszug)ts
// editor.component.ts (excerpt)
import { Component, NgZone, inject, signal } from '@angular/core';

export class EditorComponent {
  private readonly zone = inject(NgZone);

  readonly selectedTag = signal('');
  readonly savedAt = signal<Date | null>(null);

  private mount(host: HTMLElement): void {
    // The canvas fires pointer events continuously while a block is being
    // dragged. Creating the editor outside the zone keeps those from
    // scheduling a change-detection pass on every frame.
    this.zone.runOutsideAngular(() => {
      const editor = grapesjs.init({ container: host, height: '100%' });

      // Re-enter only where editor state has to become Angular state.
      editor.on('component:selected', (component) => {
        this.zone.run(() => this.selectedTag.set(component.get('tagName') ?? ''));
      });

      editor.on('storage:end:store', () => {
        this.zone.run(() => this.savedAt.set(new Date()));
      });

      this.editor = editor;
    });
  }
}

// Zoneless applications (provideZonelessChangeDetection) have no zone to
// leave. Set signals from the same editor events and drop NgZone entirely.
  • Erstelle den Editor innerhalb von NgZone.runOutsideAngular(), damit die Canvas-Aktivität keine Änderungserkennung auslöst.
  • Geben Sie mit NgZone.run() nur in den Handlern erneut ein, bei denen der Editor-Zustand Angular werden muss – eine Auswahl, ein Speicherzeitstempel, ein Validierungsfehler.
  • Wenn in deinem UI nichts den Editor-Status widerspiegelt, musst du nie wieder eingeben.
  • In einer zonenlosen Anwendung (provideZonelessChangeDetection) gibt es keine Zone, die man verlassen kann: Schreibe auf Signale aus demselben Editor und verliere NgZone komplett.

Eine Faustregel

Verlasse die Zone einmal bei der Initialisierung. Tritt knapp wieder ein, bei den spezifischen Ereignissen, die dein Interface abhört. Das Wrappen jedes Editor-Callbacks in NgZone.run() gibt genau das Änderungserkennungs-Churn zurück, das du aus der Zone verlassen hast, um es zu vermeiden.

Serverseitiges Rendering

Funktioniert GrapesJS mit Angular SSR?

Der Editor läuft im Browser. GrapesJS baut seine Canvas aus echtem DOM – es misst Elemente, erstellt ein iframe und hängt Dokumentlistener an – daher muss die Initialisierung im Browserkontext erfolgen und darf nicht während eines Serverrenderings ausgeführt werden. Routen, die lediglich veröffentlichte Ausgaben anzeigen, bleiben unbeeinträchtigt: das ist einfaches HTML und CSS und rendert auf dem Server wie alles andere.

editor.component.tsts
// editor.component.ts — browser-only initialisation
import {
  AfterViewInit,
  Component,
  ElementRef,
  OnDestroy,
  PLATFORM_ID,
  ViewChild,
  inject,
} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import type { Editor } from 'grapesjs';

export class EditorComponent implements AfterViewInit, OnDestroy {
  @ViewChild('editorHost') private host!: ElementRef<HTMLDivElement>;

  private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
  private editor?: Editor;

  async ngAfterViewInit(): Promise<void> {
    if (!this.isBrowser) return;

    // A dynamic import keeps the editor out of the server bundle and off the
    // critical path of every route that never opens it.
    const { default: grapesjs } = await import('grapesjs');
    this.editor = grapesjs.init({ container: this.host.nativeElement });
  }

  ngOnDestroy(): void {
    this.editor?.destroy();
  }
}
  • Guard-Initialisierung, sodass es nur im Browser läuft – sowohl der Plattform-Check als auch der darunterliegende Render-Hook machen das.
  • Importiere die Bibliothek dynamisch, damit sie nie ins Server-Bundle gerät.
  • Rendere während des Serverdurchlaufs einen Platzhalter derselben Größe, um eine Layout-Verschiebung beim Erscheinen des Editors zu vermeiden.
  • Routen zu veröffentlichen, die nur gespeicherte HTML und CSS rendern, benötigen überhaupt keinen Schutz.

Die Render-Hook-Alternative

Neuere Angular-Versionen bieten einen Render-Hook, dessen Rückrufe nur auf Browserplattformen und niemals auf dem Server laufen. Wo er verfügbar ist, gibt er dieselbe Garantie ohne Plattformprüfung und passt natürlich zu signalbasierten View-Abfragen.

editor.component.ts (Render-Hook)ts
// The same guard, expressed with the render hook instead of a platform check.
import { Component, ElementRef, afterNextRender, viewChild } from '@angular/core';
import type { Editor } from 'grapesjs';

@Component({ selector: 'app-editor', template: '<div #editorHost></div>' })
export class EditorComponent {
  private readonly host = viewChild.required<ElementRef<HTMLDivElement>>('editorHost');
  private editor?: Editor;

  constructor() {
    // afterNextRender callbacks run on browser platforms only — they never
    // execute on the server, so no isPlatformBrowser check is needed.
    afterNextRender(async () => {
      const { default: grapesjs } = await import('grapesjs');
      this.editor = grapesjs.init({ container: this.host().nativeElement });
    });
  }
}

Überprüfe, welche dieser Varianten deine Angular-Version unterstützt, bevor du dich entscheidest. Beide Muster existieren, um dieselbe Regel durchzusetzen – der Editor initialisiert im Browser und sonst nirgendwo. angular.dev

Anatomie

Baue einen Angular-Seitengenerator mit GrapesJS

Ein Page Builder ist nicht ein einziges Feature. Es sind neun Subsysteme, die miteinander übereinstimmen müssen, und GrapesJS liefert alle neun aus. Das ist der Überblick; die Produktentscheidungen – Preisgestaltung, Rollen, Vorlagen, Veröffentlichungsworkflow – gehören zum unten verlinkten speziellen Leitfaden.

  • GrapesJS

    Canvas

    Die editierbare Oberfläche, die in eigenem iframe gerendert wird, sodass Seitenstile nicht in dein Admin-UI gelangen können.

  • GrapesJS

    Komponenten

    Das typisierte Modell hinter jedem Element: was es ist, was es akzeptiert und welche Einstellungen es offenlegt.

  • GrapesJS

    Block Manager

    Die Palettenbenutzer ziehen von . Ein Block ist ein benannter Inhalt, der einem Komponententyp zugeordnet wird.

  • GrapesJS

    Style Manager

    Das CSS-Bearbeitungspanel, konfigurierbar bis hin dazu, welche Eigenschaften ein bestimmter Benutzer ändern darf.

  • Plugin

    Asset Manager

    Bild- und Medienauswahl. Der dahinterliegende Upload-Endpunkt gehört dir, daher verbinden Plugins hier meist mit einem Anbieter.

  • Deine Angular-App

    Speichermanager

    Laden und speichern. GrapesJS definiert den Vertrag und ruft dein API auf – die Persistenz selbst gehört deiner Anwendung.

  • GrapesJS

    Befehle

    Benannte, aufrufbare Aktionen – rückgängig, vorschauen, exportieren und alles, was du für die Symbolleiste registrierst.

  • GrapesJS

    Gerätemanager

    Die responsiven Breakpoints, an denen die Canvas vorschauen und gestaltet werden können.

  • GrapesJS

    Panels

    Der eigene Chrome des Editors – Buttons, Toolbars und Regionen, alle austauschbar, wenn der Editor in einer bestehenden Shell sitzt.

Wer baut esGrapesJSPluginDeine Angular-App

Acht der neun werden mit dem Editor oder einem Plugin ausgeliefert. Das, was unvermeidlich dir gehört, ist der Speicher – der auch über deine Nutzer, deine Berechtigungen und deine Datenbank Bescheid wissen muss.

Baue einen kompletten Angular-Seitenbauer
Wo er sitzt

Der Editor in einer Angular-Anwendung

Von außen nach innen gelesen ist das die gesamte Integration. Alles oberhalb der GrapesJS-Instanz ist gewöhnliches Angular; alles darunter ist der Editor, der seine eigene Arbeit in einem iframe erledigt, den du nie anfassen musst.
  1. Angular application
  2. Editor route (lazy-loaded)
  3. EditorComponent
  4. GrapesJS instance
  5. Canvas iframe

Wenn der Editor von einem anderen Ort in der Anwendung erreichbar sein muss – einer Symbolleiste in der Shell, einer Seitenliste, einem Vorschaufenster – verschieben Sie die Initialisierung in einen injizierbaren Dienst und lassen Sie Komponenten mit diesem kommunizieren, anstatt direkt mit dem Editor.

Die Canvas ist ein echter iframe. Das ist absichtlich: Seitenstile dürfen nicht in deine Admin-Oberfläche gelangen und deine Anwendungsstile dürfen nicht auf die bearbeitete Seite gelangen, was genau die Isolation ist, die du willst, wenn beide von verschiedenen Leuten entworfen wurden.

Ausgabe

Was erzeugt GrapesJS?

Drei Artefakte, und deren Vermischung führt zu echtem Datenverlust. Projektdaten sind das editierbare Dokument; HTML und CSS sind das veröffentlichte Ergebnis. Sie sind nicht austauschbar, da der Export von Natur aus verlustbehaftet ist.

Projektdaten

Ein JSON-Dokument, das Komponenten, Stile, Seiten und Assets enthält. Das ist das, was du speicherst, damit ein Editor die Seite genau so öffnen kann, wie sie gelassen wurde. Behandle es als Quelle der Wahrheit.

HTML

Das Markup für die Seite, wie sie ausgeliefert wird. Speichere es zusammen mit den Projektdaten, sodass beim Veröffentlichen kein Editor starten muss und eine Seite auch dann rendern kann, wenn der Editor nicht verfügbar ist.

CSS

Die Stile, die der Redakteur für diese Seite erstellt hat, angepasst an die von ihm verwalteten Regeln. Serviere es mit dem HTML – die beiden sind ein Output, aufgeteilt auf zwei Dateien.

output.tsts
// Three outputs, three different jobs. Do not use one where you need another.

const projectData = editor.getProjectData(); // editable state → store this
const html = editor.getHtml();               // markup         → publish this
const css = editor.getCss();                 // styles         → publish this

// Reopening a saved project restores components, styles, pages and assets.
// Re-parsing exported HTML does not — it loses everything not in the markup.
editor.loadProjectData(projectData);

Das Reparsen von exportiertem HTML in den Editor ist kein Ersatz für Projektdaten: Es verliert Komponententypen, traits, Seitenstruktur und alles andere, was im Markup nicht ausdrückbar war. GrapesJS ist ein Editor, kein CMS oder ein Backend – wohin die Artefakte gelangen und was danach mit ihnen passiert, ist die Entscheidung Ihrer Anwendung.

Persistenz

Speichere GrapesJS-Projekte in deiner Angular-Anwendung

GrapesJS definiert den Vertrag; du stellst die Endpunkte bereit. Registrieren Sie einen benannten Speicher mit zwei asynchronen Methoden, und der Editor ruft sie auf Abruf oder automatisch, wenn sich Bearbeitungen ansammeln. Alles rund um Authentifizierung, Mieter und Berechtigungen bleibt dort, wo es hingehört: in Ihren Angular-Diensten und Ihrem API.
  1. Angular
  2. GrapesJS Storage
  3. HttpClient → REST API
  4. Your backend
  5. Database
project-storage.service.tsts
// project-storage.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import type { Editor, ProjectData } from 'grapesjs';

@Injectable({ providedIn: 'root' })
export class ProjectStorage {
  private readonly http = inject(HttpClient);

  /** Registers a named storage the editor can then autosave into. */
  register(editor: Editor, projectId: string): void {
    editor.Storage.add('gjs-api', {
      load: () =>
        firstValueFrom(this.http.get<ProjectData>(`/api/projects/${projectId}`)),

      store: async (data) => {
        await firstValueFrom(
          this.http.put(`/api/projects/${projectId}`, {
            project: data,          // what the editor reopens
            html: editor.getHtml(), // what the site renders
            css: editor.getCss(),
          })
        );
      },
    });
  }
}
editor.component.ts (Auszug)ts
// Point the editor at the storage you just registered.
this.storage.register(editor, projectId);

grapesjs.init({
  container: host,
  storageManager: {
    type: 'gjs-api',      // the name passed to Storage.add()
    autosave: true,
    autoload: true,
    stepsBeforeSave: 5,   // batch edits instead of a request per keystroke
  },
});

Der Editor kommuniziert nie mit deiner Datenbank. Er ruft die beiden von dir registrierten Methoden auf, die HttpClient aufrufen, der dein API unter denselben Interceptoren und der gleichen Authentifizierung wie jede andere Anfrage in der Anwendung aufruft.

Persistenz-Checkliste

Fünf Entscheidungen, die vor dem ersten Rettungswurf getroffen werden müssen

  1. 1
    Projekt JSON

    Speichere das editierbare Dokument

    Behalten Sie die getProjectData()-Ausgabe bei, während der Datensatz eines Benutzers wieder geöffnet wird. Speichern Sie die exportierten HTML und CSS im selben Schreibabschnitt, sodass die Veröffentlichung keinen zweiten Roundtrip hat.

  2. 2
    Autosave

    Batch-Writes, nicht streamen

    autosave mit stepsBeforeSave verwandelt einen Schnitt in eine einzige Anfrage. Stellen Sie die Schrittzahl auf Ihr API ein, nicht auf die Demo-Standardeinstellung.

  3. 3
    Last

    Autoload, oder explizit laden

    autoload lässt den Editor beim Start abrufen. Das explizit Laden gibt dir stattdessen einen Ort, um deinen eigenen Spinner anzuzeigen und einen 403 als Anwendungsfehler statt als Editor-Fehler zu behandeln.

  4. 4
    Versionen

    Verlauf serverseitig bewahren

    Der Rückgängig-Stack lebt im Browser und stirbt mit dem Tab. Wenn Nutzer die Seite von gestern wiederherstellen müssen, ist das eine Versionstabelle in deiner Datenbank, die im Store geschrieben wurde.

  5. 5
    Veröffentlichen

    Getrenntes Sparen und Veröffentlichen

    Ein Speicherstand aktualisiert den Entwurf. Das Veröffentlichen fördert eine bestimmte Version auf die Live-Route – einen eigenständigen Endpunkt mit eigener Berechtigungskontrolle.

Medien

Verwalten Sie Bilder und Assets

Der Asset Manager ist ein Picker, kein Dateidienst. Richten Sie ihn auf einen Endpunkt in Ihrer Angular-Anwendung und er wird die Dateien dort POSTEN; alles danach – Validierung, Speicherung, Größenänderung, Auslieferung – steht dir zur Verfügung, was es ermöglicht, die bereits vorhandene Medienbibliothek wiederzuverwenden.

editor.component.ts (Auszug)ts
// Assets go to your endpoint, under your auth — GrapesJS only POSTs files.
grapesjs.init({
  container: host,
  assetManager: {
    upload: `/api/projects/${projectId}/assets`,
    uploadName: 'files',
    multiUpload: true,
    credentials: 'include',
    headers: { 'X-Requested-With': 'XMLHttpRequest' },
    // Off by default is the safer choice: base64 images end up inside saved
    // markup and make every page row grow without limit.
    embedAsBase64: false,
    // Returning false aborts the upload before the request leaves the browser.
    beforeUpload: (files: File[]) =>
      files.every((file) => file.size <= MAX_UPLOAD_BYTES && ALLOWED_TYPES.has(file.type)),
  },
});

editor.on('asset:upload:start', () => this.uploading.set(true));
editor.on('asset:upload:end', () => this.uploading.set(false));
editor.on('asset:upload:error', (error) => this.uploadError.set(String(error)));
  • Uploads werden mit deinen Headern und Zugangsdaten auf deinen Endpunkt geschickt, sodass die gleiche Authentifizierung wie der Rest der App gilt.
  • Überprüfen Sie, bevor die Anfrage den Browser verlässt und erneut auf dem Server ist – die clientseitige Kontrolle ist Bequemlichkeit, keine Kontrolle.
  • Gib die gespeicherten URLs von deinem Endpunkt zurück; der Editor fügt diese URLs in die Seite ein, also müssen sie die sein, die du bereitstellen willst.
  • Schalte das Base64-Einbetten aus, es sei denn, du möchtest Bilder in gespeicherte Markup-Dateien einfügen, wo jede gespeicherte Seite unbegrenzt wächst.
  • Bereite Assets aus einem CDN- oder Objektspeicher bereit und halte den Editor auf die öffentliche URL ausgerichtet, nicht auf den Ursprung deiner Anwendung.
  • Laden Sie große Bibliotheken über einen paginierten Endpunkt, anstatt dem Editor bei der Initialisierung Tausende von Assets zu geben.

Nichts davon schreibt ein bestimmtes Backend vor. S3, Cloudinary, eine Laravel-Festplatte oder ein Ordner auf einem Server erfüllen alle denselben Vertrag: eine Datei akzeptieren, eine URL zurückgeben.

Erweiterung des Editors

Erstellen Sie benutzerdefinierte Komponenten

Das Registrieren eines Komponententyps ist der Weg, wie der Editor mehr über dein Produkt erfährt. Ein benutzerdefinierter Typ erhält ein eigenes Einstellungspanel, eigene Drag-and-Drop-Regeln und einen Block in der Palette – so arbeiten Content-Teams mit Konzepten aus deiner Domain statt mit rohen Divs.

pricing-table.type.tsts
// pricing-table.type.ts — an application-specific building block.
import type { Editor } from 'grapesjs';

export function registerPricingTable(editor: Editor): void {
  editor.Components.addType('pricing-table', {
    // Lets the editor recognise the type again when a saved page is reparsed.
    isComponent: (el) => el.classList?.contains('pricing-table'),
    model: {
      defaults: {
        tagName: 'section',
        attributes: { class: 'pricing-table' },
        droppable: false,
        // Traits become the right-hand settings panel for this component.
        traits: [
          { type: 'text', name: 'plan', label: 'Plan name' },
          { type: 'number', name: 'price', label: 'Price' },
          { type: 'checkbox', name: 'featured', label: 'Highlight' },
        ],
        components: `
          <h3 data-gjs-type="text">Team</h3>
          <p data-gjs-type="text">$29 / month</p>
        `,
      },
    },
  });

  // A block is how the component reaches the user: one entry in the palette.
  editor.Blocks.add('pricing-table', {
    label: 'Pricing table',
    category: 'Commerce',
    content: { type: 'pricing-table' },
  });
}

Typische anwendungsspezifische Komponenten

  • Hero-Abschnitt
  • Preistabelle
  • Produktkarte
  • Formular
  • Navigation
  • Testimonial
  • Funktionsraster
  • Alles, was speziell auf dein SaaS zutrifft

Zwei Dinge machen einen benutzerdefinierten Typ nützlich. isComponent ermöglicht es dem Editor, deinen Typ erneut zu erkennen, wenn eine gespeicherte Seite neu geparst wird, und traits wird zum Einstellungspanel, das ein nicht-technischer Nutzer tatsächlich bearbeitet. Beides lohnt sich, selbst für eine einfache Komponente geschrieben zu werden.

Verlängern Sie es

GrapesJS mit Plugins erweitern

Ein Plugin ist eine Funktion, die die Editor-Instanz empfängt, daher ist hier nichts Angular-spezifisch – alles, was für GrapesJS veröffentlicht wird, funktioniert innerhalb einer Angular-Komponente. Das sind die vier Lücken, die Teams zuerst finden, nachdem der Editor gemountet ist.

Bauen oder übernehmen

Den Editor selbst bauen oder GrapesJS verwenden?

Einen visuellen Editor zu erstellen ist in Angular möglich – es ist eine bekannte Menge an Arbeit, keine unmögliche. Die Frage ist, welche Teile davon du für die Lebensdauer des Produkts besitzen möchtest.

FunktionSelbst bauenGrapesJS
CanvasSelbst bauenEnthalten
Drag & DropSelbst bauenEnthalten
KomponentenSelbst bauenEnthalten
BlöckeSelbst bauenEnthalten
StylingSelbst bauenEnthalten
AssetsSelbst bauenEnthalten
StorageSelbst bauenErweiterbar – du lieferst die Endpunkte
PluginsBaue ein Ökosystem aufPlugin-Architektur
Angular-IntegrationNativ in deiner AppIntegration über eine Komponente
LizenzDeine eigeneBSD-3-Clause — $0 to use

"Bauen" ist keine Kritik – jede einzelne davon ist baubar. Es ist eine Aussage darüber, wer sie danach pflegt.
Die Fakten in dieser Tabelle bestätigen 2026-09-02 erneut.

Angular gibt dir das Anwendungs-Framework. GrapesJS gibt dir die visuelle Bearbeitungs-Engine.

Integrationswahl

Brauche ich für GrapesJS einen Angular-Wrapper?

Nein, und es lohnt sich zu verstehen, warum die Frage aufkommt. GrapesJS veröffentlicht einen offiziellen React-Wrapper, daher erwarten Entwickler vernünftigerweise ein Angular-Äquivalent. Es gibt keinen. Das lässt drei Ansätze übrig, und für die meisten Teams ist die erste die richtige Antwort.

Empfohlen

Direkte Integration

Schreiben Sie die Komponente selbst – die auf dieser Seite umfasst ungefähr dreißig Zeilen. Sie besitzen den Lebenszyklus, die Änderungserkennungsstrategie und den Upgrade-Weg, und zwischen Ihrer App und dem Editor API gibt es keinen Drittanbieter.

  • Keine zusätzliche Abhängigkeit, um mit Angular-Veröffentlichungen auf dem neuesten Stand zu bleiben
  • Vollzugriff auf jede Editor-Option und jedes Ereignis
  • Upgrades für GrapesJS landen sofort
Drittanbieter

Ein Community-Wrapper

Community-Pakete existieren und können die erste Stunde verkürzen. Analysiere eines wie jede Abhängigkeit: Überprüfe, wer es pflegt, welche Angular- und GrapesJS-Versionen es deklariert und wie kürzlich es veröffentlicht wurde.

  • Nicht vom GrapesJS-Projekt gepflegt
  • Fügt eine Versionsbeschränkung sowohl für Angular als auch GrapesJS hinzu
  • Es lohnt sich, das mit der aktuellen Core-Version abzugleichen, bevor man es adoptiert.
  • ngx-grapesjs 21.0.0 · 2026-01-15 · peer grapesjs ^0.22.4
Für größere Apps

Ihr eigener Service-Wrapper

Sobald mehrere Komponenten den Editor benötigen – eine Symbolleiste, eine Seitenliste, ein Vorschaufenster – verschieben Sie die Initialisierung in einen injizierbaren Service. Es ist eine direkte Integration mit einer Naht und hält den Editor aus Ihrem Komponentenbaum heraus.

  • Ein Ort, der Init und Destroy besitzt
  • Der Editor-Zustand erreicht Komponenten als Signale oder Observablen
  • Testbar ohne Montage des Editors

Die GrapesJS-Organisation unterhält einen React-Wrapper und kein Angular-Paket. Jede Angular-Bibliothek, die du findest, ist Drittanbieter, egal wie nützlich – behandle sie als Abhängigkeitswahl, nicht als Teil von GrapesJS.

Architektur

Produktionsarchitektur für GrapesJS + Angular

Eine Demo ist eine Komponente. Ein Produkt besteht aus vier Ebenen, und der Editor ist nur eine davon. Das ist die Form, auf die sich die meisten Teams konzentrieren, sobald der Editor echte Nutzer hinter sich hat.

  1. Angular-Anwendung

    Die Shell, der Router und eine faul geladene Route, die den Editor besitzt.

    • App Shell
    • Router
    • Editor-Route (Lazy Loading)
  2. GrapesJS-Instanz

    Einmal konfiguriert, bei der Initialisierung: Welche Komponenten existieren, was kann gezogen werden, was kann gestaltet werden.

    • Komponenten
    • Blöcke
    • Stile
    • Assets
    • Befehle
  3. Anwendungsdienste

    Die Angular-Schicht zwischen dem Editor und deinem API – und der einzige Ort, an dem die Autorisierungsentscheidungen clientseitig getroffen werden.

    • Auth
    • API-Client
    • Berechtigungen
    • Veröffentlichung
  4. Backend

    Wo die Artefakte wohnen und wo jede Erlaubnis wirklich durchgesetzt wird.

    • Projekte
    • Assets
    • Nutzer
    • Versionen

Nichts in diesem Diagramm ist GrapesJS-spezifisch, außer der zweiten Stufe. Das ist der Punkt: Der Editor ist eine Komponente in einer gewöhnlichen Angular-Anwendung, und der Rest ist die Architektur, die du sowieso gebaut hättest.

Sehen Sie, wie Teams dies als Produkt verpacken
Leistung

Leistungsüberlegungen

Der Editor ist ein großes Stück Software, das in deiner Anwendung läuft. Das sind die Bereiche, in denen das tatsächlich etwas kostet.

Initialisieren Sie nur, wenn es nötig ist

Erstellen Sie den Editor, wenn der Bearbeitungsweg geöffnet wird, nicht beim Start der Anwendung. Nutzer, die nie bearbeiten, sollten niemals dafür bezahlen.

Lazy-Load die Strecke

Ein dynamischer Import hält GrapesJS aus dem ursprünglichen Bündel heraus, das den größten Hebel auf dieser Liste ist.

Bleib aus der Zone heraus

Erstellen Sie den Editor mit runOutsideAngular, damit Canvas-Zeigerereignisse nicht bei jedem Frame die Änderungserkennung auslösen.

Binde die Asset-Bibliothek

Gib dem Editor keine vollständige Medienbibliothek bei init. Führe deine API durch und lade Assets, wie es der Picker verlangt.

Beobachten Sie den Komponentenbaum

Sehr tiefe oder sehr repetitive Seitenstrukturen machen Auswahl, Styling und Rückgängigmachen langsamer. Komponente bevorzugen gegenüber tief verschachtelten Verpackungen.

Batch-Speicherungen

Verwenden Sie stepsBeforeSave, damit ein Burst von Bearbeitungen zu einer Anfrage wird und nicht mehr zu einer Anfrage pro Tastendruck.

Keines davon hat eine versprochene Zahl. Messen Sie Ihre eigene Editor-Route vor und danach – die Größe jedes Effekts hängt von Ihren Blöcken, Ihren Seiten und Ihrem API ab.

Sicherheit

Sicherheitsaspekte

Ein visueller Editor verwandelt Nutzer in Autoren von HTML, die deine Anwendung dann bedient. Behandle alles, was aus dem Editor kommt, als Benutzereingabe, denn genau das ist es.

Sanitise veröffentlichte HTML

Die Ausgabe des Editors kann beliebige Markups enthalten, einschließlich Skripte, wenn benutzerdefinierte Codeblöcke aktiviert sind. Reinigen Sie serverseitig, bevor sie an andere als den Autor ausgeliefert werden.

Validiere hochgeladene Assets

Überprüfen Sie Typ und Größe auf dem Server, nicht nur in beforeUpload. Speichern Sie Uploads außerhalb des Web-Roots und servieren Sie sie von einem separaten Ursprung aus.

Speicherendpunkte authentifizieren

Lade- und Speicherrouten sind gewöhnliche API-Endpunkte. Sie benötigen die gleiche Authentifizierung wie alles andere in der Anwendung.

Erzwingen von Berechtigungen serverseitig

Das Ausblenden eines Veröffentlichen-Buttons im Angular UI ist die Präsentation. Die entscheidende Prüfung ist die am Endpunkt.

Vertrauen Sie niemals auf Projektdaten

Das gespeicherte Projekt JSON kann manipuliert werden. Validiere es beim Laden, anstatt davon auszugehen, dass es das von dir geschriebene Dokument ist.

Scope Tenants explizit

In einem Multi-Tenant-Produkt leiten Sie den Projektbesitzer aus der Sitzung auf dem Server ab – niemals aus einer ID im Anfrage-Body.

Bevor du verschiffst

Produktionscheckliste Angular + GrapesJS

Zwölf Dinge, die eine funktionierende Demo von einer Route unterscheiden, die echte Nutzer unterscheidet, können bereitgestellt werden.

  1. GrapesJS installieren

    Abhängigkeit hinzugefügt und das Stylesheet wird entweder global oder in der Editor-Komponente registriert.

    Lies den Abschnitt
  2. Initialisieren nach Ansichtserstellung

    grapesjs.init() läuft in ngAfterViewInit gegen einen Container mit einer aufgelösten Höhe.

    Lies den Abschnitt
  3. Zerstöre den Editor korrekt

    editor.destroy() in ngOnDestroy, verifiziert, indem man ohne Lecks weg und zurück navigiert.

    Lies den Abschnitt
  4. Browser-only-Initialisierung verwalten

    Bewacht, sodass während eines Server-Renderings nichts läuft, mit einem Platzhalter an seiner Stelle.

    Lies den Abschnitt
  5. Verbindungsspeicher

    Ein benannter Speicher ist registriert, autosave optimiert und das Projekt JSON wird zusammen mit HTML und CSS gespeichert.

    Lies den Abschnitt
  6. Assets konfigurieren

    Uploads zeigen auf dein Endpunkt, base64 wird aktiviert, Validierung auf beiden Seiten.

    Lies den Abschnitt
  7. Fügen Sie benutzerdefinierte Komponenten hinzu

    Die eigenen Bausteine Ihres Produkts sind als Typen registriert, mit traits und Paletteneinträgen.

    Lies den Abschnitt
  8. Füge die Plugins hinzu, die du brauchst

    Blöcke, Rich Text und Export wurden bewusst gewählt statt angesammelt.

    Lies den Abschnitt
  9. Implementierung von Berechtigungen

    Wer bearbeiten darf, wer veröffentlichen darf – das wird auf dem Server durchgesetzt, was sich im UI widerspiegelt.

    Lies den Abschnitt
  10. Veröffentlichte Inhalte desinfizieren

    Serverseitige Sanierung zwischen dem Speichern einer Seite und deren Bereitstellung an Besucher.

    Lies den Abschnitt
  11. Fehler im Monitor-Editor

    Editor-Ausnahmen wurden an derselben Stelle gemeldet wie der Rest deiner Angular-Fehler.

  12. Test-responsives Schneiden

    Der Gerätemanager wurde auf einem Touch-Gerät trainiert, nicht nur im Desktop-Browserfenster.

    Lies den Abschnitt
FAQ

GrapesJS und Angular: häufige Fragen

Nächster Schritt

Baue deinen Angular-Visual-Editor mit GrapesJS

Beginnen Sie mit dem Open-Source-Visual-Editor, verbinden Sie ihn mit Ihrer Angular-Anwendung und erweitern Sie ihn mit den Plugins und Integrationen, die Ihr Produkt benötigt.

Entwickler

Leg los.

Installiere das Paket, kopiere die Komponente und lasse heute Nachmittag einen Editor auf einer Angular-Route laufen.

Leg los
Erweitern

Plugins durchsuchen

Blöcke, Speicher, Rich Text, Assets, Export und E-Mail – die Stücke, die du sonst selbst schreiben würdest.

Durchsuchen von Plugins
Team

Planen Sie das Produkt

Scopen Sie den Editor als Feature Ihrer Anwendung: Rollen, Vorlagen, Veröffentlichung und Versionierung.

Beginnen Sie mit einem Briefing

Baue die Anwendung mit Angular. Baue den visuellen Editor mit GrapesJS. Erweitere ihn mit GJS.Market-Plugins.