如何為 Angular 應用程式加入拖放式電郵編輯器:完整教學
Add a drag and drop email builder to your Angular app (full tutorial)
教學示範如何在 Angular 應用程式加入拖放式電郵編輯器 angular-email-craft,並逐步設定設計儲存、重新載入及 HTML 輸出。電郵 HTML 可由伺服器使用 email-craft-sdk 產生;圖片上載與 AI 助手則可連接自選的 S3 儲存空間及 AI 服務,相關密鑰留在伺服器。
Every SaaS eventually hears the same request: "Can we design our own emails inside your app?"
Most teams either spend months building an editor, or pay per seat for one they can't control.
In this post we add a complete drag and drop email builder to a fresh Angular app, step by step. It's the written version of a 9 minute video where every step is filmed for real, nothing cut.
🎥 Watch the video: https://www.linkedin.com/feed/update/urn:li:activity:7519630925253615616
What you need: an Angular 17 to 22 app (ours is a fresh ng new on Angular 22) and Node 18 or newer.
1. Create an account and a token
Sign up at mailkaro.com. Sign in is passwordless: every sign in uses a one time code sent to your email, and an account can only be signed in on one device at a time.
Then open Embed & API, click Generate new token, and choose which features the token exposes, for example:
- Save, export and history
- Uploads and assets
- AI Assistant
You get two keys:
| Key | Starts with | Where it goes |
|---|---|---|
| Publishable | pk_live_ |
your frontend |
| Secret | sk_live_ |
your server only, never in frontend code |
2. Install and render
npm install angular-email-craft
Put the publishable key in src/environments/environment.ts:
export const environment = {
mailkaroKey: "pk_live_...",
};
Then render the editor. It's one standalone component:
import { Component } from "@angular/core";
import { EmailEditorComponent } from "angular-email-craft";
import { environment } from "../environments/environment";
@Component({
selector: "app-root",
imports: [EmailEditorComponent],
template: `<email-editor [token]="token" minHeight="100vh" />`,
})
export class App {
token = environment.mailkaroKey;
}
That's the full editor: the canvas in the middle, blocks and settings on the right.
Tip: the starter
app.htmlis a placeholder page. Use the inlinetemplateabove (or emptyapp.html) so the editor gets the whole page.NgModule app? Import
EmailEditorModulefromangular-email-craftinstead.
3. Save and reopen a design
The (ready) output hands you the editor. Add two buttons. exportHtml gives you the design (JSON describing everything on the canvas) and the HTML. Store the design: that's what reopens the email later.
import { Component } from "@angular/core";
import { EmailEditorComponent, type EditorApi } from "angular-email-craft";
import { environment } from "../environments/environment";
@Component({
selector: "app-root",
imports: [EmailEditorComponent],
template: `
<button (click)="save()">Save</button>
<button (click)="open()">Open</button>
<email-editor [token]="token" minHeight="90vh" (ready)="editor = $event" />
`,
})
export class App {
token = environment.mailkaroKey;
editor?: EditorApi;
save() {
this.editor?.exportHtml(({ design, html }) => {
localStorage.setItem("design", JSON.stringify(design)); // your database in a real app
});
}
open() {
const saved = localStorage.getItem("design");
if (saved) this.editor?.loadDesign(JSON.parse(saved));
}
}
Called without a callback, exportHtml() returns a promise, so const { design, html } = await this.editor!.exportHtml() works too.
4. Merge tags
Each merge tag has a label, the value your email service fills in, and a sample:
mergeTags = {
first_name: { label: "First name", value: "{{first_name}}", sample: "Alex" },
company: { label: "Company", value: "{{company}}", sample: "Acme" },
};
<email-editor [token]="token" [mergeTags]="mergeTags" minHeight="90vh" (ready)="editor = $event" />
In a text block, your users pick Insert merge tag. The tag lands as a labelled pill, so they can see exactly where each field goes.
5. Render email-safe HTML on your server
Sending needs finished HTML, and you can make it on your own server from the saved design. In a server folder next to your app:
npm install email-craft-sdk express
// server/index.mjs
import express from "express";
import { renderDesign, renderMjml } from "email-craft-sdk";
const app = express();
app.use(express.json({ limit: "2mb" }));
app.post("/render", (req, res) => {
const html = renderDesign(req.body.design, { fullDocument: true });
const mjml = renderMjml(req.body.design);
res.json({ html, mjml });
});
app.listen(3001, () => console.log("Server on http://localhost:3001"));
There's no headless browser, no network call, and no key needed. It runs inside your backend.
6. Decide what each user can do
The plan unlocks when both keys work together: the publishable key in the browser, and the secret key on your server. Add one route:
import { Mailkaro } from "email-craft-sdk";
const mailkaro = Mailkaro.fromEnv(); // reads MAILKARO_SERVER_KEY (sk_live_...)
app.get("/mailkaro/embed-runtime", async (_req, res) => {
res.json(await mailkaro.resolveRuntime());
});
Point the editor at it with runtimeUrl:
<email-editor
[token]="token"
runtimeUrl="/mailkaro/embed-runtime"
[mergeTags]="mergeTags"
minHeight="90vh"
(ready)="editor = $event" />
In development, let the Angular dev server forward these routes to Node, so they run on the same origin as your app. Create proxy.conf.json:
{
"/mailkaro": { "target": "http://localhost:3001" },
"/render": { "target": "http://localhost:3001" }
}
and point ng serve at it in angular.json (under serve):
"options": { "proxyConfig": "proxy.conf.json" }
Your server answers, and the secret key never leaves it.
7. Your own storage and your own AI
Add your S3 bucket and AI provider key to the server's .env, then three small routes:
import multer from "multer";
app.post("/mailkaro/ai", async (req, res) => {
res.json(await mailkaro.ai(req.body.method, req.body.input));
});
const upload = multer();
app.post("/mailkaro/upload", upload.single("file"), async (req, res) => {
res.json(await mailkaro.uploadAsset({
buffer: req.file.buffer,
filename: req.file.originalname,
contentType: req.file.mimetype,
scopeId: "demo", // your tenant or workspace id
}));
});
app.get("/mailkaro/assets", async (_req, res) => {
res.json(await mailkaro.listAssets({ scopeId: "demo" }));
});
Now the image library has an Amazon S3 tab with your bucket's images, and new uploads go straight into it. The AI assistant runs on your own key and builds emails right on the canvas. The keys stay on the server, and the browser never sees them.
Already on angular-email-editor?
Install angular-email-craft and change the import:
// before
import { EmailEditorComponent } from "angular-email-editor";
// after
import { EmailEditorComponent } from "angular-email-craft";
Your template, your (loaded) / (ready) handlers and your exportHtml / loadDesign calls keep working, and every design you saved with Unlayer opens as it is.
The whole loop
- One token
- One standalone component
- Two calls to save and reopen
- One function to render on your server
- One route that decides what each user can do
The free plan covers the editor itself. Pro unlocks storage, AI and more, and the first month is free with no card.
- Try it without signing up: mailkaro.com/demo
- Angular docs: mailkaro.com/docs/angular
- npm: angular-email-craft
Next in the series: the same setup in Vue and plain JavaScript.
Which one does your product need first: the email editor, or the AI writing the emails? Tell me in the comments.
來源:DEV Community · dev.to