Skip to content

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 ​

sh
npm install @docmentis/udoc-viewer
sh
npm install @docmentis/udoc-editor

The 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:

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:

ts
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 ​

ts
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();
    }
}
ts
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 pane class.
  • ngAfterViewInit creates the viewer, and ngOnDestroy destroys it.
  • create is asynchronous, so the component can be destroyed before it resolves. #gone records 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:

html
<app-document-viewer src="/report.pdf" />
css
.pane {
    display: block;
    height: 100vh;
}

The example ​

examples/angular shows the viewer and the editor side by side in an Angular app.