Section:

Customize Salesforce with WebViewer and Lightning Web Component (LWC)

Our Salesforce implementation integrates Apryse WebViewer inside a Lightning Web Component (LWC).

The integration allows for bi‑directional communication:

  • LWC to WebViewer via postMessage to the iframe.
  • WebViewer to LWC via postMessage back to the parent window.

This allows Salesforce data and WebViewer actions to stay in sync (e.g., loading documents, updating fields, tracking annotations).

This article highlights entry point of editing when you want to manipulate the data being sent to WebViewer and the information being sent back. This guide also includes how Salesforce and WebViewer communicate.

Architecture

  1. Lightning Web Component (LWC)
    • Hosts an <iframe> that loads WebViewer.
    • Uses postMessage to send commands and Salesforce payloads into WebViewer.
    • Listens for messages coming back from WebViewer.
  2. WebViewer Config File
    • Runs inside the iframe.
    • Registers window.addEventListener('message', ...) to receive messages from LWC.
    • Uses a switch statement to map incoming message types to WebViewer APIs.
    • Uses parent.postMessage(...) to send information back to the LWC.

Message Flow

Example for Load Document Flow

  1. LWC sends LOAD_DOCUMENT.
  2. WebViewer receives and loads document.
  3. WebViewer sends back DOCUMENT_LOADED.
  4. LWC receives the DOCUMENT_LOADED message.

LWC to WebViewer, Send Messages

In the LWC, you’ll typically:

  1. Reference the iframe.
  2. Call postMessage on its contentWindow.

Example for Send a Command from LWC

javascript

apryseWvInstance.js

1// Inside your LWC JS controller.
2sendCommandToWebViewer() {
3 const iframe = this.template.querySelector('iframe');
4 if (!iframe || !iframe.contentWindow) {
5 return;
6 }
7 const message = {
8 type: 'LOAD_DOCUMENT',
9 payload: {
10 recordId: this.recordId,
11 documentUrl: this.documentUrl,
12 // Any other Salesforce data needed by WebViewer.
13 }
14 };
15 iframe.contentWindow.postMessage(message, '*');
16}

Typical fields include:

  • Type: String that identifies the action (used in WebViewer’s switch).
  • Payload: Object containing Salesforce-specific data (record IDs, URLs, metadata, etc.).

WebViewer, Listen for Messages from LWC

In the WebViewer config file (e.g., config_apex.js), you’ll set up a message listener.

Basic Listener Structure

javascript

config_apex.js

1window.addEventListener('message', async (event) => {
2 const { type, payload } = event.data || {};
3 if (!type) {
4 return;
5 }
6 switch (type) {
7 case 'LOAD_DOCUMENT':
8 await handleLoadDocument(payload);
9 break;
10 case 'SET_READONLY':
11 await handleSetReadonly(payload);
12 break;
13 // add additional cases as needed
14 default:
15 console.warn('Unknown message type received in WebViewer:', type);
16 break;
17 }
18});

Example for Call WebViewer APIs

javascript

config_apex.js

1async function handleLoadDocument(payload) {
2 const { documentUrl, recordId } = payload;
3 await instance.UI.loadDocument(documentUrl);
4
5 // Optionally, notify LWC that the document has loaded.
6 parent.postMessage(
7 {
8 type: 'DOCUMENT_LOADED',
9 payload: { recordId, documentUrl }
10 },
11 '*'
12 );
13}

You can map any message type to WebViewer APIs. For example:

  • Load a document.
  • Jump to a page.
  • Toggle read-only.
  • Import/export annotations.
  • Trigger save events.

WebViewer to LWC, Send Messages Back

From within the WebViewer iframe, parent gives you access to the LWC’s window.
You can send information back using parent.postMessage.

Common Use Cases

  • Notify LWC that:
    • Document finished loading.
    • User created/edited/deleted annotations.
    • User saved or signed a document.
  • Send structured payloads (e.g., XFDF, page numbers, flags).

Example, Send Data to LWC

javascript

apryseWvInstance.js

1function notifyAnnotationsChanged(annotations) {
2 // "annotations" could be raw annotation objects or exported XFDF/string.
3 parent.postMessage(
4 {
5 type: 'ANNOTATIONS_UPDATED',
6 payload: {
7 annotations,
8 timestamp: Date.now()
9 }
10 },
11 '*'
12 );
13}

You might call this from WebViewer event handlers:

javascript

config_apex.js

1instance.Core.annotationManager.addEventListener('annotationChanged', async (annotations, action) => {
2 notifyAnnotationsChanged({ annotations, action });
3});

LWC, Receive Messages from WebViewer

On the Salesforce side, you’ll register a message event listener on window in the LWC.

Example, Handle Messages from WebViewer in LWC

javascript

apryseWvInstance.js

1// LWC JS controller
2connectedCallback() {
3 window.addEventListener('message', this.handleMessage);
4}
5disconnectedCallback() {
6 window.removeEventListener('message', this.handleMessage);
7}
8handleMessage(event) {
9 // Optional: validate origin here
10 // if (event.origin !== this.expectedOrigin) return;
11 const { type, payload } = event.data || {};
12 if (!type) return;
13 switch (type) {
14 case 'DOCUMENT_LOADED':
15 this.onDocumentLoaded(payload);
16 break;
17 case 'ANNOTATIONS_UPDATED':
18 this.onAnnotationsUpdated(payload);
19 break;
20 default:
21 // Ignore unknown message types
22 break;
23 }
24}
25onDocumentLoaded({ recordId, documentUrl }) {
26 // e.g., update component state, show toast, etc.
27}
28onAnnotationsUpdated({ annotations, timestamp }) {
29 // e.g., save to Salesforce via Apex, update UI, etc.
30}

Message Contract Design

To keep things maintainable:

  • Use clear type names (e.g., LOAD_DOCUMENT, SAVE_ANNOTATIONS, SET_READONLY).
  • Structure payload as a stable contract:
    • Document info (Blob, URL).
    • Salesforce identifiers (recordId, related object Ids).
    • Flags and options (read-only, mode).
  • Document all supported message types in your guide, including:
    • Direction (LWC to WebViewer, WebViewer to LWC).
    • Expected payload fields.
    • Behavior triggered.

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales