Angular
The viewer and the editor each fill an element you give them. In Angular, that can be the component's own host element, and the viewer is created in it once the component's view is ready.
Install
npm install @docmentis/udoc-viewernpm install @docmentis/udoc-editorThe engine's files
Angular's builder bundles your app with esbuild, which doesn't copy the engine's binary and fonts beside it as Vite and webpack do. Copy them from @docmentis/udoc-wasm yourself, among the build's assets in angular.json:
"assets": [
{ "glob": "**/*", "input": "public" },
{ "glob": "wasm/*.wasm", "input": "node_modules/@docmentis/udoc-wasm/dist", "output": "udoc-wasm" },
{ "glob": "fonts/**/*", "input": "node_modules/@docmentis/udoc-wasm/dist", "output": "udoc-wasm" },
{ "glob": "geography/**/*", "input": "node_modules/@docmentis/udoc-wasm/dist", "output": "udoc-wasm" }
]Then point the viewer at them with wasm.baseUrl:
import type { WasmLocateOptions } from "@docmentis/udoc-viewer";
/**
* Where the app serves the engine's binary and fonts, which angular.json copies
* from `@docmentis/udoc-wasm`. Angular's builder bundles with esbuild, which
* leaves the `new URL(…, import.meta.url)` that finds them as it is rather than
* emitting the files it names, so the viewer and the editor are pointed at the
* app's copy.
*/
export const wasm: WasmLocateOptions = { baseUrl: "/udoc-wasm/" };Without this, the viewer still works, but loads the engine from jsDelivr.
The component
import { Component, ElementRef, inject, input, type AfterViewInit, type OnDestroy } from "@angular/core";
import { UDocViewer } from "@docmentis/udoc-viewer";
import { wasm } from "./wasm";
/** A viewer filling its element, opening `src`. */
@Component({
selector: "app-document-viewer",
template: "",
host: { class: "pane" },
})
export class DocumentViewer implements AfterViewInit, OnDestroy {
readonly src = input.required<string>();
readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);
#viewer?: UDocViewer;
// Destroyed before `create` resolves, the component destroys the viewer it made.
#gone = false;
ngAfterViewInit(): void {
UDocViewer.create(this.#host.nativeElement, { wasm })
.then(async (viewer) => {
if (this.#gone) return viewer.destroy();
this.#viewer = viewer;
await viewer.open(this.src());
})
.catch((error: unknown) => console.error("The viewer didn't open the document:", error));
}
ngOnDestroy(): void {
this.#gone = true;
this.#viewer?.destroy();
}
}import { Component, ElementRef, inject, input, type AfterViewInit, type OnDestroy } from "@angular/core";
import { UDocEditor } from "@docmentis/udoc-editor";
import { wasm } from "./wasm";
/** An editor filling its element, opening `src`. */
@Component({
selector: "app-document-editor",
template: "",
host: { class: "pane" },
})
export class DocumentEditor implements AfterViewInit, OnDestroy {
readonly src = input.required<string>();
readonly #host = inject<ElementRef<HTMLElement>>(ElementRef);
#editor?: UDocEditor;
// Destroyed before `create` resolves, the component destroys the editor it made.
#gone = false;
ngAfterViewInit(): void {
UDocEditor.create(this.#host.nativeElement, { wasm })
.then(async (editor) => {
if (this.#gone) return editor.destroy();
this.#editor = editor;
await editor.open(this.src());
})
.catch((error: unknown) => console.error("The editor didn't open the document:", error));
}
ngOnDestroy(): void {
this.#gone = true;
this.#editor?.destroy();
}
}- The component has no template: its host element is the viewer's container, and takes the
paneclass. ngAfterViewInitcreates the viewer, andngOnDestroydestroys it.createis asynchronous, so the component can be destroyed before it resolves.#gonerecords that, and the viewer created after it is destroyed at once.
Use it
The viewer fills its element, so give the element a height, here through the pane class. A host element is inline by default, so make it a block too:
<app-document-viewer src="/report.pdf" />.pane {
display: block;
height: 100vh;
}The example
examples/angular shows the viewer and the editor side by side in an Angular app.