Angular
The editor in an Angular app: one component opens it with createEditor from @lettrove/embed,
and a small Express server mints its tokens.
You need a project's two test keys from lettrove.com → Settings → Embed (step 1 of the Quickstart), and the Node.js version the current Angular CLI asks for (it tells you when it starts).
1. The token server
The editor opens with a short-lived token that your server mints with the secret key, so the key never reaches a browser. In your product this is one route in the server you already have.
mkdir -p lettrove-angular/server
cd lettrove-angular/server
npm init -y
npm install express @lettrove/node
LETTROVE_SECRET_KEY=lt_sk_test_paste_yours_here
import express from 'express';
import { Lettrove } from '@lettrove/node';
const lettrove = new Lettrove({ secretKey: process.env.LETTROVE_SECRET_KEY });
const app = express();
// The page asks this route for a token whenever the editor needs one.
app.post('/lettrove-token', async (req, res) => {
// In your product: your own id for the signed-in person, from your session.
const userId = 'demo-user';
try {
const { token } = await lettrove.tokens.create({
user: { id: userId },
origin: req.get('origin'), // the page the editor opens on
});
res.set('Cache-Control', 'no-store').json({ token });
} catch (error) {
console.error(error);
res.status(500).json({ error: error.message, code: error.code });
}
});
app.listen(3000, () => console.log('Token server on http://localhost:3000'));
Start it, and leave it running:
node --env-file=.env server.mjs
2. The Angular app
In a second terminal, from the lettrove-angular folder:
npx @angular/cli@latest new app --defaults --skip-git --routing=false --ssr=false --style=css
cd app
npm install @lettrove/embed
Send the app's /lettrove-token requests to the token server:
{
"/lettrove-token": { "target": "http://localhost:3000" }
}
Replace src/app/app.ts. Put your publishable key where it says so.
import { AfterViewInit, Component, ElementRef, OnDestroy, viewChild } from '@angular/core';
import { createEditor, type EditorHandle } from '@lettrove/embed';
async function getToken() {
const response = await fetch('/lettrove-token', { method: 'POST' });
return (await response.json()).token;
}
@Component({
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App implements AfterViewInit, OnDestroy {
private readonly container = viewChild.required<ElementRef<HTMLDivElement>>('editor');
private editor: EditorHandle | null = null;
async ngAfterViewInit() {
this.editor = await createEditor({
container: this.container().nativeElement,
publishableKey: 'lt_pk_test_paste_yours_here',
getToken,
on: { 'design:saved': ({ designId }) => console.log('saved', designId) },
});
}
ngOnDestroy() {
this.editor?.destroy();
}
async exportHtml() {
const email = await this.editor?.exportHtml();
console.log(email?.html);
}
}
Replace src/app/app.html and src/app/app.css:
<div class="page">
<button (click)="exportHtml()">Export HTML</button>
<div #editor class="editor"></div>
</div>
.page { height: 100vh; display: flex; flex-direction: column; }
.editor { flex: 1; }
And in src/styles.css, remove the page's own margin:
body { margin: 0; }
3. Run it
npx ng serve --proxy-config proxy.conf.json
Open http://localhost:4200. Add a Heading from the panel on the left;
the browser's console prints saved and the design's id. Keep that id to open the same design
again: pass it as designId. Click Export HTML and the console prints the whole email,
compiled for every inbox.
Next
- If the box shows a code instead of the editor: the Quickstart's checklist.
@lettrove/embed: every option and method ofcreateEditor, and the events.- Getting designs out · Theming · Test and live keys.