template-editor.component.ts Angular
import { Component } from "@angular/core";
import { ReportDesignerComponent } from "@broadpaper/angular";
import type { DataSource, Issue, ReportTemplate, Theme } from "@broadpaper/core";
@Component({
selector: "app-template-editor",
standalone: true,
imports: [ReportDesignerComponent],
template: `
<bp-report-designer
[template]="template"
[dataSources]="dataSources"
[sampleData]="sampleData"
[theme]="theme"
[themes]="themes"
[onSave]="save"
(templateChange)="draft = $event"
(validate)="issues = $event"
style="height: 100vh"
/>
`
})
export class TemplateEditorComponent {
/** The saved design, or undefined for a new one. */
template?: ReportTemplate;
/** The current, unsaved design. */
draft?: ReportTemplate;
issues: Issue[] = [];
theme!: Theme;
themes: Theme[] = [];
dataSources: DataSource[] = [
{
id: "policy",
label: "Policy",
schema: {
reference: { type: "string", label: "Policy number" },
renewsOn: { type: "date", label: "Renews on" },
premium: { type: "currency", label: "Annual premium", currency: "GBP" },
sections: {
type: "array",
label: "Cover",
itemSchema: {
type: "object",
fields: {
name: { type: "string", label: "Section" },
limit: { type: "currency", label: "Limit", currency: "GBP" },
excess: { type: "currency", label: "Excess", currency: "GBP" }
}
}
}
}
}
];
sampleData = [
{
id: "typical",
label: "A renewing policy",
data: {
policy: {
reference: "HX-4417",
renewsOn: "2026-11-01",
premium: 1840,
sections: [
{ name: "Buildings", limit: 750000, excess: 500 },
{ name: "Contents", limit: 60000, excess: 250 }
]
}
}
}
];
// An arrow property, not a method. The component hands this straight to the
// editor, so it must not depend on how it is called — a method here loses
// `this` and fails the first time somebody presses Save.
save = async (t: ReportTemplate): Promise<void> => {
const res = await fetch(`/api/templates/${t.id}`, {
method: "PUT",
headers: { "content-type": "application/json" },
body: JSON.stringify(t)
});
if (!res.ok) throw new Error(`Could not save (${res.status})`);
};
}
[onSave] is an arrow property, not a method
The component passes the reference straight through to the editor, so it must not depend on how it is
called. Written as a class method it loses this and fails the first time anybody presses Save —
in production, on somebody else's document.
Throwing in save is the error path
The toolbar shows “Save failed” with your message. saveDescription is the other half: one
sentence saying where saving actually goes, because only the host knows, and saying nothing invites the
user to assume the safest possible answer.