Angular
Angular 17.1 or later, standalone components or NgModules. Three steps: install the packages, copy the fonts with an asset glob, and wrap the element in a typed component.
npm install ./vendor/rd-designer-1.0.0.tgz ./vendor/rd-core-1.0.0.tgz ./vendor/rd-fonts-1.0.0.tgz
npm install ./vendor/rd-fonts-cjk-1.0.0.tgz # optional: Chinese, Japanese, Korean
A typed wrapper component#
A typed wrapper component with @Inputs and @Outputs. templateChange pairs with the template input, so [(template)] two-way binding works:
import { CUSTOM_ELEMENTS_SCHEMA, Component, EventEmitter, Input, Output } from '@angular/core';
import '@rd/designer'; // registers <report-designer>
import type { Template, TemplateChangeDetail } from '@rd/designer';
@Component({
selector: 'app-report-designer',
standalone: true,
// Lets Angular accept the <report-designer> tag and bind its properties and events.
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<report-designer
[template]="template"
[schema]="schema"
[lang]="lang"
[attr.font-base-url]="fontBaseUrl"
(template-change)="onTemplateChange($event)"
(save)="onSave($event)"
></report-designer>
`,
styles: `
:host, report-designer { display: block; height: 100%; }
`,
})
export class ReportDesignerComponent {
@Input() template: Template | null = null;
@Input() schema: object | null = null;
@Input() lang = '';
@Input() fontBaseUrl = '/rd-fonts/';
@Output() templateChange = new EventEmitter<Template>();
@Output() saved = new EventEmitter<Template>();
onTemplateChange(e: Event): void {
this.template = (e as CustomEvent<TemplateChangeDetail>).detail.template;
this.templateChange.emit(this.template);
}
onSave(e: Event): void {
this.saved.emit((e as CustomEvent<TemplateChangeDetail>).detail.template);
}
}
Use it in a page#
import { Component, OnInit, input, signal } from '@angular/core';
import type { Template } from '@rd/designer';
import { ReportDesignerComponent } from './report-designer.component';
@Component({
selector: 'app-template-editor',
standalone: true,
imports: [ReportDesignerComponent],
template: `
<header>{{ dirty() ? 'Unsaved changes' : 'All changes saved' }}</header>
<app-report-designer
[(template)]="template"
[schema]="schema"
(templateChange)="dirty.set(true)"
(saved)="save($event)"
></app-report-designer>
`,
styles: `:host { display: flex; flex-direction: column; height: 100vh; } app-report-designer { flex: 1; min-height: 0; }`,
})
export class TemplateEditorComponent implements OnInit {
readonly templateId = input.required<string>();
readonly dirty = signal(false);
template: Template | null = null;
schema: object = bolSchema;
async ngOnInit(): Promise<void> {
this.template = await loadTemplate(this.templateId());
}
async save(t: Template): Promise<void> {
await saveTemplate(this.templateId(), t);
this.dirty.set(false);
}
}
Copy the fonts#
Copy both font packages into one served folder with angular.json asset globs, so no copy script is needed (drop the second glob if you
didn't install @rd/fonts-cjk):
"assets": [
{ "glob": "**/*", "input": "public" },
{ "glob": "**/*", "input": "node_modules/@rd/fonts/fonts", "output": "rd-fonts" },
{ "glob": "**/*", "input": "node_modules/@rd/fonts-cjk/fonts", "output": "rd-fonts" }
],
"allowedCommonJsDependencies": ["pako"]
Raise the initial bundle budget in angular.json, or lazy-load the editor route (loadComponent: () => import('./template-editor.component').then(m => m.TemplateEditorComponent)).
pako is a CommonJS dependency of the PDF engine. Listing it silences the build warning.
Angular with NgModules#
For applications that still use NgModules, declare a non-standalone component and add CUSTOM_ELEMENTS_SCHEMA to the module:
import { CUSTOM_ELEMENTS_SCHEMA, Component, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import '@rd/designer';
import type { Template, TemplateChangeDetail } from '@rd/designer';
@Component({
selector: 'app-legacy-editor',
standalone: false,
template: `<report-designer [template]="template" font-base-url="/rd-fonts/" (save)="onSave($event)"></report-designer>`,
styles: `report-designer { display: block; height: 100vh; }`,
})
export class LegacyEditorComponent {
template: Template | null = null;
onSave(e: Event): void {
void saveTemplate('bol', (e as CustomEvent<TemplateChangeDetail>).detail.template);
}
}
@NgModule({
declarations: [LegacyEditorComponent],
imports: [BrowserModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
bootstrap: [LegacyEditorComponent],
})
export class LegacyEditorModule {}