在 Angular 中构建拖放页面构建器比你想象的要简单,这要归功于 GrapesJS。GrapesJS 是一个开源的网页构建框架,允许你通过可视化拖放编辑器创建 HTML 页面。在这个教程中,我们将把 GrapesJS 集成到 Angular 应用中,创建一个代码最少的页面构建器。你将学会如何在Angular中设置GrapesJS,使用官方的GrapesJS Basic Blocks插件来处理现成内容块,导出设计好的HTML/CSS,甚至保存到后端。我们将使用 Angular v20(截至 2025 年 10 月的最新稳定版本和最新的 GrapesJS 库,以确保你能保持最新)。
完成这份逐步指南后,你将拥有一个可用的 Angular 页面构建器,用户可以将基本块(文本、图片、列等)拖到画布上,使用 GrapesJS 内置编辑器进行样式化,并保存结果。让我们开始吧!
前提条件
在继续之前,请确保你已经安装并设置好以下内容:
- Node.js和 npm ——安装包含 npm 的最新 LTS Node.js 版本。
- Angular CLI – 全局安装,使用 npm install -g @angular/cli。本教程使用Angular CLI 20。
- 基础Angular知识 ——理解Angular中的组件、模块和服务。
- 一个代码编辑器 ——比如VS Code、WebStorm等,用来编辑你的项目文件。
- 互联网连接 ——GrapesJS将通过npm安装。
步骤1:搭建Angular项目
首先,使用Angular CLI创建一个新的Angular项目。我们将项目命名为 grapesjs-pagebuilder(你可以选择任何名字):
ng New Grapesjs-Pagebuilder
按照CLI提示操作(你可以跳过添加路由演示,直接用CSS作为样式表格式)。项目生成后,进入项目目录并启动开发服务器:
CD Grapesjs-Pagebuilder ng serve -o
这会在 http://localhost:4200/ 打开默认的Angular欢迎页面。接下来,我们将为页面构建器设置结构。生成一个新的 Angular 组件,用于托管 GrapesJS 编辑器。我们称它为 PageBuilderComponent:
ng generate component 页面构建器
这将创建page-builder.component.ts及其HTML/CSS文件,并更新应用模块。在这个教程中,我们希望页面构建组件占据整个应用。打开 src/app/app.component.html,并用<app-page-builder></app-page-builder>选择器替换其内容(并移除默认模板文本)。这确保了我们的页面构建组件被渲染为主页。
现在我们已经准备好一个全新的 Angular 应用,用于 GrapesJS 集成。
步骤2:安装GrapesJS和Basic Blocks插件
Angular 项目准备好后,让我们安装 GrapesJS 和官方 的 Basic Blocks 插件。Basic Blocks 插件提供了一组预构建的内容块(文本、图片、列等),我们可以拖拽它们。在你的项目目录中打开终端并运行:
NPM install Grapesjs Grapesjs-Blocks-Basic --save
这样会把GrapesJS和基本块插件添加到你的项目依赖中。安装后,我们需要确保 Angular 知道 GrapesJS 资产(CSS/JS 文件)并处理 TypeScript 定义。
在angular.json中包含GrapesJS资产
GrapesJS 自带 CSS,我们还有一个插件 JS 可以加入。打开angular.json,找到你应用的构建选项(在项目 -> YOUR_APP_NAME -> 架构师 -> 建造 ->选项下)。例如,将 GrapesJS 文件添加到样式和脚本数组中,例如:
“样式”: [ “node_modules/葡萄 styles.css /dist/css/grapes.min.css”, “脚本”: [ “node_modules/葡萄/dist/grapes.min.js”, “node_modules/葡萄-块-basic/dist/index.js” ]
这样可以确保 GrapesJS 核心的 CSS 和脚本(包括基本模块插件脚本)在应用运行时被加载。CSS 会为 GrapesJS 编辑器(画布、面板等)做样式,而 JS 脚本则会让 grapesjs 和插件全局可用。
设置TypeScript定义
如果你尝试导入 Angular TypeScript 文件中的 GrapesJS,可能会遇到错误,因为 GrapesJS 不包含 TypeScript 类型定义。为避免任何 TypeScript 编译错误 (例如, “找不到模块 'grapesjs' 的声明文件”),创建定义文件。在项目 src/ 文件夹中,添加一个名为 grapesjs.d.ts 的新文件,内容如下:
宣布模块“葡萄”; 声明模块“Grapesjs-Blocks-BASIC”;
这样可以声明模块,所以 TypeScript 会把它们当作任何模块来处理,导入时不会抱怨。现在我们准备在 Angular 代码中使用 GrapesJS。
(如果你用的是TypeScript并喜欢更强的类型,GrapesJS提供了一个usePlugin辅助工具,帮助插件提升类型安全。为了简化,我们先进行基本设置。)
步骤3:将GrapesJS集成到Angular组件中
现在进入主要部分:将GrapesJS编辑器嵌入我们的Angular组件中。我们将在创建的 PageBuilderComponent 中初始化 GrapesJS 编辑器。
3.1 组件模板设置
page-builder.component.html。我们需要一个容器元素让GrapesJS编辑器挂载。添加一个 div,作为编辑器的画布容器。我们还在内部包含一个简单的标题作为初始内容(GrapesJS 可以导入容器内已有的 HTML)。此外,我们还会添加一个“保存”按钮,可以触发内容导出:
<按钮(点击)=“savePage()”>保存页面</button> <div #gjsContainer> <h2>我的第一个GrapesJS页面</h2> </div>
这里:
- #gjsContainer 是一个模板引用,我们将用它来获取组件类中的DOM元素。
- 容器内的<h2>My First GrapesJS Page</h2>是 GrapesJS 最初加载的示例内容(你可以在这里放置任何占位内容)。
3.2 组件类:初始化 GrapesJS
开 page-builder.component.ts。我们会导入 GrapesJS 和插件,然后在组件的生命周期钩子中初始化编辑器。
首先,导入必要的模块,并为编辑器实例声明一个属性:
从 '@angular/core' 导入 { AfterViewInit, Component, ElementRef, OnDestroy, ViewChild };
从“GrapesJ”导入葡萄;
从“grapesjs-blocks-basic”导入basicBlocks; Basic Blocks 插件
...(其他导入,比如服务和Angular相关内容)
@Component({
selector: 'app-page-builder',
templateUrl: './page-builder.component.html',
styleUrls: ['./page-builder.component.css']
})
export 类 PageBuilderComponent 实现了 AfterViewInit, OnDestroy {
@ViewChild('gjsContainer', { static: false }) editorContainer!: ElementRef<HTMLDivElement>;
编辑:ANY; 我们将在这里存储 GrapesJS 编辑器实例
constructor(private pageService: PageService) { }
解释:
- 我们使用 @ViewChild('gjsContainer') 来捕获模板中对 <div #gjsContainer> 的引用
- .这让我们能够访问 GrapesJS 渲染的实际 DOM 元素。
- 我们实现了 AfterViewInit 来在视图准备好后初始化 GrapesJS(以便 editorContainer 被填充),并用 OnDestroy 在组件被销毁时清理编辑器。
- 我们导入 grapesjs 和顶部的插件(grapesjs-blocks-basic)。插件导入给了我们一个可以传递给GrapesJS的函数。
现在,在该类中:
ngAfterViewInit(): void {
Initialize GrapesJS editor
this.editor = grapesjs.init({
将编辑器绑定到div元素
容器:this.editorContainer.nativeElement, 使用ViewChild元素:contentReference[oaicite:9]{index=9}:contentReference[oaicite:10]{index=10}
fromElement: true, 导入容器内部的初始内容 HTML:contentReference[oaicite:11]{index=11}
高度:'700px',
width:'auto',
暂时禁用本地存储(无自动保存)
storageManager:false,
包含 Basic Blocks 插件
:[basicBlocks],pluginsOpts: {
[basicBlocks]: {
你可以在这里自定义插件选项(例如,包含哪些方块)
}
}
});
GrapesJS 编辑器现在已挂载并可见。
}
让我们来分解一下我们传递给 grapesjs.init() 的重要配置选项:
- container – 用于挂载编辑器的元素或选择器。这里我们传递了 ViewChild 中的 原生 DOM 元素 (this.editorContainer.nativeElement)
- .这会把 GrapesJS 绑定到 模板中的 <div #gjsContainer>。
- fromElement – 告诉 GrapesJS 从容器元素内的 HTML 加载编辑器内容
- .因为我们在div里放了一个<h2>My First GrapesJS Page</h2>,编辑会从画布上的组件开始。
- height / width ——设置编辑器的尺寸。我们用700px的高度(你可以调整或用'100vh')和'自动'宽度来填充容器宽度。
- storageManager – GrapesJS 有存储管理器(例如保存到 localStorage)。我们把这个设置为false,暂时禁用自动保存
- .这样可以防止 GrapesJS 自动保存状态(我们会手动处理保存)。
- 插件 ——一系列可用的插件。我们这里包含了导入的 basicBlocks 插件
- .该插件向 GrapesJS 块管理器添加了一组基本内容块(文本、链接、图片、视频、列数等)。
- pluginsOpts – 插件的选项。我们为basicBlocks提供了一个空的options对象(使用插件函数作为键)。默认情况下,基本方块插件会以默认设置添加所有方块(列、文本、图片等)
- . (你可以自定义——例如,要排除像Map这样的块,可以指定块:['column1','column2',...,'video'],在选项中省略“map”。
- .在我们的情况下,我们会使用所有默认的方块。)
以下是我们使用的关键GrapesJS配置选项及其功能总结表:

调用 grapesjs.init(...)后,GrapesJS 编辑器的界面会出现 在组件内部。默认情况下,GrapesJS 加载时包含一组面板和按钮。比如,你会看到顶部有选项栏(比如切换方块面板、撤销/重做等),中间有画布(<h2>出现的地方),右侧是样式/设置面板。左侧会有一个块面板,里面包含插件提供的 Basic 块(文本、链接、图片、视频等)。你可以把这些块拖到画布上来构建页面。
提示: 默认情况下,GrapesJS 的方块面板是隐藏的,直到你点击左上角工具栏上的“方块”按钮。点击 “方块” 图标切换方块面板,查看可用的拖放方块。GrapesJS 的默认界面包含这个切换按钮。如果需要,你可以配置GrapesJS默认显示块面板(例如使用自定义面板或blockManager配置中的appendTo选项),但目前使用默认行为是可以的。
3.3 清理:在组件销毁中销毁编辑器
为了防止组件卸载时出现内存泄漏或问题,当组件被销毁时,最好销毁 GrapesJS 编辑器实例。我们为组件实现了ngOnDestroy:
ngOnDestroy(): void {
if (this.editor) {
this.editor.destroy();
}
}
}
这调用了编辑器的 destroy() 方法,以正确卸载事件监听器和释放资源。GrapesJS 为此提供了 editor.destroy() 功能(它在内部触发 destroy 事件以进行清理)。现在,如果用户离开组件,我们的 GrapesJS 实例会干净地终止。
此时,GrapesJS 已完全集成到 Angular 中。你可以运行ng serve并进入应用验证:你应该会看到GrapesJS编辑器界面,画布里有你的样本标题和工具栏。试着从块面板拖拽一个块(比如文本或图片块)到画布里,确保一切正常。
步骤4:从GrapesJS导出HTML和CSS
GrapesJS 的一个强大功能是允许你将用户创建的页面导出为干净的 HTML 和 CSS。我们在模板中添加了 “保存页面” 按钮;现在我们将实现逻辑,当点击这个按钮时,从 GrapesJS 中提取 HTML/CSS。
在我们的 PageBuilderComponent 类中,我们已经有一个 savePage() 方法连接到按钮上。让我们来实现它:
savePage(): void {
从 GrapesJS 编辑器
获取 HTML 和 CSS const html = this.editor.getHtml();
const css = this.editor.getCss();
console.log('导出 HTML:', html);
console.log(“导出CSS:”,css);
可选地,将内容发送到后端
this.pageService.savePage(html, css).subscribe({
next: response => console.log('页面成功保存!', response),
error: err => console.error('Error saving page:', err)
});
}
这里我们使用 GrapesJS Editor 的 API 方法 getHtml() 和 getCss() 从画布中获取当前的 HTML 标记和 CSS 样式。GrapesJS 会返回画布内部(设计页面)的 HTML 字符串,以及所有已应用样式(包括类等)的 CSS 字符串。我们先把它们记录到控制台,然后再联系服务来真正保存它们。
此时,如果你点击运行中的应用中的“保存页面”按钮,你应该会在浏览器控制台看到生成的HTML和CSS打印出来。例如,HTML可能会显示一个包含你的块和内容的<div>结构,而CSS可能包含GrapesJS为样式创建的类。
步骤5:将页面内容发布到后端(Angular服务)
在现实应用中,你很可能会想把用户的页面设计保存到后端服务器(例如存储在数据库或文件系统中)。我们将通过创建一个 Angular 服务来模拟,将 HTML/CSS 发送到后端 API 端点。
5.1 创建服务
生成一个新的服务(如果你还没做的话),叫做PageService:
ng 生成服务页面
这就产生了 page.service.ts。在这项服务中,我们将使用 Angular 的 HttpClient 将页面内容发布到 API。确保在你的AppModule(app.module.ts)中导入HttpClientModule,这样HttpClient就能使用。
现在,打开 page.service.ts 并实现保存页面的方法:
import { Injectable } 来自 '@angular/core';
从 '@angular/common/http' 导入 { HttpClient };
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class PageService {
private apiUrl = '/api/pages'; URL 到后端端点(根据需要更改)
构造器(private http: HttpClient) { }
savePage(html: 字符串,css: string): Observable<any> {
const payload = { html, css };
将 HTML/CSS 发布到后端(替换为你实际的 API 端点),
返回 this.http.post(this.apiUrl, payload);
}
}
在这段代码中:
- 我们定义了一个apiUrl(例如,'/api/pages'),它应该指向你的后端端点,负责保存页面数据。在真实应用中,用真实的URL(例如服务器上的API路由)替换它。为了演示,我们使用相对路径。
- savePage方法接受HTML和CSS字符串,将它们打包成JSON负载,并利用 HttpClient.post 将数据发送到服务器。
- 我们返回一个可观测量,以便调用者订阅并处理响应或错误。
确保该服务在根中提供(通过 @Injectable providedIn 属性)。我们之前通过构造器将这项服务注入了我们的PageBuilderComponent。
5.2 在组件中使用该服务
我们已经将 PageService 注入组件,并在 savePage() 方法中调用了 this.pageService.savePage(html, css).subscribe(...)。点击按钮时,请求就会被发送。在真实的应用中,你可能会在成功保存后显示成功消息或导航等。就我们而言,我们只是记录结果。
现在,当你运行应用并点击“保存页面”时,应用会向指定的端点发送带有页面内容的POST请求。你可以在浏览器的开发工具网络标签页中验证这一点。(如果你实际上没有运行该URL的后端,会出现错误——这是预期中的。实际上,你会实现后端来处理请求。目前,我们的重点是前端集成。)
结论
恭喜你!你用 GrapesJS 在 Angular 里构建了一个基础的 拖放页面构建 器。🎉
在本指南中,我们:
- 为页面构建器搭建一个Angular项目和组件(使用 Angular v20)。
- 安装了 GrapesJS 和官方的 grapesjs-blocks-basic 插件,用于现成可用的模块。
- 将 GrapesJS 集成到 Angular 组件中,并进行适当的生命周期管理(在 ngAfterViewInit 初始化,在 ngOnDestroy 中销毁)。
- 配置了GrapesJS的Essential Options,并加载了Basic Blocks插件,提供可拖拽的内容块。
- 用 GrapesJS API 导出了页面的 HTML 和 CSS,并实现了一个 Angular 服务将这些数据发送到后端。
在 Angular 中使用 GrapesJS 可以让你在利用 Angular 生态系统的同时,为终端用户提供可视化的页面构建体验。你可以通过添加更多GrapesJS插件(例如富文本编辑器、自定义块等)、处理保存页面的用户认证,甚至构建专门针对你应用的自定义块库来扩展这个基础。GrapesJS 非常可扩展——你可以创建自定义块插件或集成样式管理器,让用户更自由地调整样式。
下一步: 你也可以考虑 GrapesJS 的其他功能,比如风格管理器和特性管理器,这些在我们的设置中自动启用(编辑器右侧栏可以更改所选元素的样式等)。你还可以配置存储管理器来持久化数据(例如,持久化到localStorage,或通过GrapesJS的远程存储选项直接保存到后端),而不是手动保存按钮。如果想做更高级的设置,可以考虑使用 GrapesJS 预设(比如 grapesjs-preset-webpage),它捆绑了一系列插件,提供一个功能齐全的页面编辑器——不过在这个教程中,我们只停留在基础上,展示了底层的运作方式。
希望这个教程对你有所帮助。祝你编程愉快,享受用Angular和GrapesJS打造精彩页面构建器的过程!🚀
