Public API
Angular has no forwardRef imperative handle the way React does. Instead, PowerPointViewerComponent's programmatic API is just public methods on the component instance, reached through a template reference variable or Angular's viewChild() signal query.
import { Component, viewChild } from '@angular/core';
import { PowerPointViewerComponent } from 'pptx-angular-viewer';
@Component({
selector: 'app-editor',
standalone: true,
imports: [PowerPointViewerComponent],
template: `
<button (click)="save()">Save</button>
<button (click)="viewer().goNext()">Next Slide</button>
<button (click)="viewer().undo()">Undo</button>
<pptx-viewer #viewer [content]="content" [canEdit]="true" />
`,
})
export class EditorComponent {
readonly viewer = viewChild.required(PowerPointViewerComponent);
async save(): Promise<void> {
const bytes = await this.viewer().getContent();
// persist `bytes` (a Uint8Array)
}
}Template reference vs viewChild
#viewer in the template plus @ViewChild(PowerPointViewerComponent) viewer!: PowerPointViewerComponent works too (see the package README); viewChild() is the modern signal-based equivalent used above. Either way you get the same component instance and the same methods.
Contract shared across bindings
The methods below implement the same PowerPointViewerAPI contract (defined in ooxml-ui/pptx) that React's PowerPointViewerHandle and Vue's defineExpose surface also implement - all three framework bindings expose an equivalent API surface, just through each framework's own idiom (React forwardRef handle, Vue defineExpose, Angular public methods).
import type { PowerPointViewerAPI, ViewerMode } from 'pptx-angular-viewer';Methods
Serialization
| Method | Signature | Description |
|---|---|---|
getContent | () => Promise<Uint8Array> | Serialises the current document to .pptx bytes on demand. When editing, serialises the edited deck (templates merged back). |
Navigation
| Method | Signature | Description |
|---|---|---|
goTo | (index: number) => void | Navigate to a specific slide (zero-based). No-op when out of range. |
goPrev | () => void | Navigate to the previous slide. |
goNext | () => void | Navigate to the next slide. |
Undo / Redo
| Method | Signature | Description |
|---|---|---|
undo | () => void | Undo the last editing action. No-op when nothing to undo. |
redo | () => void | Redo the last undone action. |
canUndo | () => boolean | Whether an undo action is available. |
canRedo | () => boolean | Whether a redo action is available. |
Zoom
| Method | Signature | Description |
|---|---|---|
getZoom | () => number | Get the current zoom level (1 = 100%). |
setZoom | (level: number) => void | Set the zoom level (clamped to 0.2 - 3.0). |
zoomIn | () => void | Zoom in by one step. |
zoomOut | () => void | Zoom out by one step. |
zoomReset | () => void | Reset zoom to 100%. |
Mode
| Method | Signature | Description |
|---|---|---|
getMode | () => string | Get the current viewer mode: 'preview', 'edit', 'present', or 'master'. |
setMode | (mode: string) => void | Switch mode programmatically. 'present' enters slideshow, 'master' enters template-edit mode, anything else returns to normal preview/edit. |
Read-only state
| Method | Signature | Description |
|---|---|---|
getActiveSlideIndex | () => number | Get the zero-based active slide index. |
setActiveSlideIndex | (index: number) => void | Set the active slide (alias of goTo). |
getSlideCount | () => number | Get the total number of slides. |
isDirty | () => boolean | Whether the document has unsaved changes. |
Slide access
Slide methods return full PptxSlide objects from pptx-viewer-core with complete type information (elements, notes, transitions, animations, etc.).
| Method | Signature | Description |
|---|---|---|
getSlides | () => readonly PptxSlide[] | Get all slides in the deck. |
getSlide | (index: number) => PptxSlide | undefined | Get a slide by zero-based index. |
getActiveSlide | () => PptxSlide | undefined | Get the currently active slide. |
Slide manipulation
| Method | Signature | Description |
|---|---|---|
addSlide | (afterIndex?: number) => void | Add a blank slide (after active by default). |
deleteSlides | (indexes: number[]) => void | Delete slides at indexes. |
duplicateSlides | (indexes: number[]) => void | Duplicate slides at indexes. |
moveSlide | (from: number, to: number) => void | Move a slide from one position to another. |
toggleHideSlides | (indexes: number[]) => void | Toggle the hidden flag on slides. |
Element access
Element methods return full PptxElement objects (discriminated union of text, shape, image, table, chart, connector, group, etc.) with complete type-specific properties.
| Method | Signature | Description |
|---|---|---|
getElements | (slideIndex?: number) => readonly PptxElement[] | Get elements (active slide by default). |
getElementById | (elementId: string, slideIndex?: number) => PptxElement | undefined | Get element by ID. |
Element manipulation
| Method | Signature | Description |
|---|---|---|
updateElement | (elementId: string, updates: Partial<PptxElement>) => void | Patch element properties. |
updateElements | (updates: readonly ElementUpdate[], options?: ElementUpdateOptions) => Promise<void> | Update elements across slides in one undo step. |
deleteElements | (elementIds: string[]) => void | Delete elements by ID. |
duplicateElement | (elementId: string) => string | undefined | Duplicate; returns new element ID. |
Inserting an element
addElement(element: PptxElement): string | undefined appends a defensive copy to the active editable slide and selects it, returning its fresh ID. Coordinates are preserved; group descendants also receive fresh IDs. Pending text is committed through the existing editor path, with normal dirty-state and Undo/Redo behavior. Synchronous edits may share a history entry, but all insertions are retained. It returns undefined while loading, after a load error, without an active slide, or in read-only/protected, preview, presentation, or template/master editing modes.
Use a self-contained model or one from the current document. With a loaded viewer in edit mode:
import { createImageElement } from 'pptx-viewer-core';
const image = createImageElement(pngDataUrl, { x: 40, y: 40, width: 160, height: 90 });
const insertedId = this.viewer().addElement(image);This method does not install clipboard listeners, fetch remote URLs, choose image dimensions, or import another document's relationships. A host-owned paste handler can read an image and call it. For a new data-URL image, use the factory above without inventing an imagePath, which denotes an existing archive part.
Loading a local image
The package also exports createImageElementFromFile(file, canvasSize, signal?). Given a local File or Blob, the slide size in pixels, and an optional AbortSignal:
import { createImageElementFromFile } from 'pptx-angular-viewer';
const image = await createImageElementFromFile(file, canvasSize, signal);The helper preserves the image bytes and returns a centred ImagePptxElement, fitted to the slide without upscaling. It resolves null for invalid/unreadable images or dimensions, cancellation, or unavailable browser APIs. The helper does not use browser APIs until called; decoding requires those APIs.
This only constructs an element: it does not change a deck, history, selection, or clipboard. After await, verify that the same document and destination slide are still active and editable, then pass a non-null result to addElement. Abort pending work when that destination is abandoned. A slide ID alone is not a document identity, and decoding success does not guarantee that every browser image format round-trips in PowerPoint. No automatic paste listener is installed.
Selection
| Method | Signature | Description |
|---|---|---|
getSelectedElementIds | () => string[] | Get IDs of currently selected elements. |
selectElements | (ids: string[]) => void | Programmatically select elements by ID. |
clearSelection | () => void | Clear the current selection. |
UI customization
The handle also carries the whole ViewerCustomizationApi, so the chrome can be customised while the viewer runs. Each call re-renders the affected UI immediately.
this.viewer()?.hideRibbonTab('draw');
this.viewer()?.lockSetting('general.userName', 'Ada Lovelace');
this.viewer()?.hideContextMenuCommand('delete');
this.viewer()?.remapShortcut('duplicate', 'Mod+Shift+D');
this.viewer()?.setFeatureEnabled('ai', false);
this.viewer()?.setPanelVisible('notes', false);
this.viewer()?.updateCustomization({ hiddenDialogs: ['print'] });
const current = this.viewer()?.getCustomization();| Method | Effect |
|---|---|
getCustomization() / setCustomization(c) / updateCustomization(patch) / resetCustomization() | Read, replace, merge or clear the whole ViewerCustomization. |
hideRibbonTab / showRibbonTab, hideToolbarButton / showToolbarButton | Ribbon tabs and top-level toolbar buttons. |
hideOptionsPage, hideOptionsSection, hideSetting (and show*) | File > Options pages, sections and settings. |
lockSetting(id, value, hidden?) / unlockSetting(id) / setSettingDefault(id, value) | Pin a setting (read-only or hidden) or set a host default. |
hideBackstagePage / hideBackstageCard (and show*) | File tab pages and action cards. |
hideContextMenuCommand / hideCanvasContextMenuCommand (and show*) | Element and empty-canvas right-click entries. |
disableShortcut / enableShortcut / remapShortcut | Editor keyboard shortcuts. |
setPanelVisible, setFeatureEnabled, setDialogAvailable | Panels, feature areas (AI, collaboration, ...) and dialogs. |
The full id reference and recipes are in the UI Customization guide.
Example: external controls
@Component({
template: `
<button (click)="viewer().goPrev()">Prev</button>
<button (click)="viewer().goNext()">Next</button>
<span>Slide {{ viewer().getActiveSlideIndex() + 1 }}</span>
<span>{{ viewer().getActiveSlide()?.elements?.length }} elements</span>
<button (click)="viewer().zoomIn()">Zoom In</button>
<button (click)="viewer().zoomOut()">Zoom Out</button>
<button (click)="viewer().undo()" [disabled]="!viewer().canUndo()">Undo</button>
<button (click)="viewer().addSlide()">Add Slide</button>
<pptx-viewer #ref [content]="content" [canEdit]="true" />
`,
})
export class ToolbarComponent {
readonly viewer = viewChild.required(PowerPointViewerComponent, {
read: PowerPointViewerComponent,
});
}getContent vs contentChange
getContent() is a pull API: serialise on demand, e.g. when a Save button is clicked. contentChange is a push event that fires with fresh bytes as the document changes. Use whichever fits your save model; they return equivalent Uint8Array content.
Openable file kinds
The package root re-exports the shared answer to "can the viewer open this file?", so a host's drop target and its <input accept> cannot disagree with the loader. Hand-rolled endsWith chains drift: every demo in this repo once shipped .pptx,.ppt,.json, which refused on drop a .pptm that File > Open inside the viewer accepted without complaint.
import {
PPTX_OPEN_ACCEPT,
PRESENTATION_OPEN_EXTENSIONS,
isSupportedPresentationFile,
isLegacyBinaryPresentation,
presentationBaseName,
savedPresentationFileName,
type SavedPresentationFormat,
} from 'pptx-angular-viewer';| Export | Type | Description |
|---|---|---|
PPTX_OPEN_ACCEPT | string | Ready-made <input type="file" accept> value: .pptx,.ppsx,.pptm,.potx,.ppt,.json. |
PRESENTATION_OPEN_EXTENSIONS | readonly string[] | The same list unjoined, for a drop target that wants to test extensions itself. |
isSupportedPresentationFile | (name?: string | null) => boolean | Cheap pre-filter for a picked or dropped file name. Extension-only; the real answer is the loader's sniff. |
isLegacyBinaryPresentation | (name?: string | null) => boolean | True for the binary PowerPoint 97-2003 family (.ppt / .pps / .pot), which the viewer reads but never writes. |
presentationBaseName | (name?: string | null, fallback?: string) => string | The file-name stem, directories and any loadable extension removed (a path like decks/report.ppt becomes report). |
savedPresentationFileName | (name?: string | null, format?: SavedPresentationFormat) => string | The name a saved copy should be offered under: report.ppt becomes report.pptx. |
SavedPresentationFormat | 'pptx' | 'ppsx' | 'pptm' | The formats the save path can produce. Binary .ppt is deliberately absent: output is always OpenXML. |
savedPresentationFileName is the one that matters on Save As. Output is always an OpenXML package, so keeping a legacy source extension would hand the user a .ppt whose bytes are a ZIP, which PowerPoint refuses to open.