Generate documents using conditionals denoted by {{if cond}} and {{endif}} clauses. Conditionals can be helpful when content in the document should only be included in certain cases.
This demo allows you to:
To add document generation capability using conditional clauses with WebViewer:
Step 1: Choose your preferred web stack
Step 2: Add the ES6 JavaScript sample code provided in this guide
Once you generate your license key, it will automatically be included in your sample code below.
Apryse collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
1// ES6 Compliant Syntax
2// GitHub Copilot v1.0, Claude 3.5 Sonnet, July 31, 2025
3// File: index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7// Document Generation using Conditionals with JSON Data & JS
8//
9// This code demonstrates how to fill a DOCX template with JSON data, this
10// template includes conditional logic to display different content based
11// on the data provided.
12// This can be seen by the red PAST DUE marking on the invoice template that
13// is wrapped by the {{if condition}} and {{endif}} tags.
14//
15
16// Default Template Document
17const defaultDoc = 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/reminder_template.docx';
18
19// JSON data for filling the template
20const sampleData = {
21 'bill_to_name': 'Joey Hansen',
22 'past_due': true,
23 'pay_by_date': '20-03-2022',
24 'ship_to_name': 'Kathryn Hoover',
25 'total_due': '250.00',
26 'total_owing': '150.00',
27 'total_paid': '100.00',
28};
29
30// Load the default template document
31const customizeUI = async (instance) => {
32 // Load DOCX template
33 await instance.Core.documentViewer.loadDocument(defaultDoc, {
34 extension: 'docx',
35 });
36};
37
38// Apply JSON data to the PDF
39const fillTemplate = async (instance) => {
40 await instance.Core.documentViewer.getDocument().applyTemplateValues(sampleData);
41};
42
43// WebViewer section
44//
45// This code initializes the WebViewer with the basic settings
46// that are found in the default showcase WebViewer
47//
48
49const searchParams = new URLSearchParams(window.location.search);
50const history = window.history || window.parent.history || window.top.history;
51const licenseKey = 'YOUR_LICENSE_KEY';
52const element = document.getElementById('viewer');
53
54WebViewer({
55 path: '/lib',
56 licenseKey: licenseKey,
57}, element).then((instance) => {
58 // Enable the measurement toolbar so it appears with all the other tools, and disable Cloudy rectangular tool
59 const cloudyTools = [
60 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT,
61 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT2,
62 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT3,
63 instance.Core.Tools.ToolNames.CLOUDY_RECTANGULAR_AREA_MEASUREMENT4,
64 ];
65 instance.UI.enableFeatures([instance.UI.Feature.Measurement, instance.UI.Feature.Initials]);
66 instance.UI.disableTools(cloudyTools);
67
68 // Set default toolbar group to Annotate
69 instance.UI.setToolbarGroup('toolbarGroup-Annotate');
70
71 // Set default tool on mobile devices to Pan.
72 // https://apryse.atlassian.net/browse/WVR-3134
73 if (isMobileDevice()) {
74 instance.UI.setToolMode(instance.Core.Tools.ToolNames.PAN);
75 }
76
77 instance.Core.documentViewer.addEventListener('documentUnloaded', () => {
78 if (searchParams.has('file')) {
79 searchParams.delete('file');
80 history.replaceState(null, '', '?' + searchParams.toString());
81 }
82 });
83
84 instance.Core.annotationManager.enableAnnotationNumbering();
85
86 instance.UI.NotesPanel.enableAttachmentPreview();
87
88 // Add the demo-specific functionality
89 customizeUI(instance).then(() => {
90 // Create UI controls after demo is initialized
91 createUIControls(instance);
92 });
93});
94
95// Function to check if the user is on a mobile device
96const isMobileDevice = () => {
97 return (
98 /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
99 window.navigator.userAgent
100 ) ||
101 /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
102 window.navigator.userAgent.substring(0, 4)
103 )
104 );
105}
106
107// Cleanup function for when the demo is closed or page is unloaded
108const cleanup = (instance) => {
109 if (typeof instance !== 'undefined' && instance.UI) {
110 // Clean up any resources if needed
111 console.log('Cleaning up complex-generation demo');
112 }
113};
114
115// Register cleanup for page unload
116window.addEventListener('beforeunload', cleanup);
117window.addEventListener('unload', cleanup);
118
119// UI section
120//
121// Helper code to add controls to the viewer holding the buttons
122// This code creates a container for the buttons, styles them, and adds them to the viewer
123//
124
125// Text input field
126const createTextInput = (fieldName, value) => {
127 const container = document.createElement('div');
128 container.className = 'input-container';
129
130 const label = document.createElement('label');
131 label.textContent = fieldName;
132 container.appendChild(label);
133
134 const input = document.createElement('input');
135 input.name = fieldName.toLowerCase().replace(/ /g, '_');
136 input.type = 'text';
137 input.value = value || '';
138 input.className = 'text-input';
139 input.onchange = (e) => {
140 sampleData[input.name] = e.target.value;
141 };
142 container.appendChild(input);
143
144 return container;
145};
146
147// Checkbox field
148const createCheckbox = (fieldName, checked) => {
149 const container = document.createElement('div');
150 container.className = 'checkbox-container';
151
152 // Create label for the checkbox
153 const label = document.createElement('label');
154 label.textContent = fieldName;
155 container.appendChild(label);
156
157 const checkbox = document.createElement('input');
158 checkbox.name = fieldName.toLowerCase().replace(/ /g, '_');
159 checkbox.type = 'checkbox';
160 checkbox.checked = checked || false;
161 checkbox.className = 'checkbox-input';
162 checkbox.onchange = (e) => {
163 sampleData[checkbox.name] = e.target.checked;
164 };
165 container.appendChild(checkbox);
166
167 return container;
168};
169
170// Fill Template Button
171const fillTemplateButton = (instance) => {
172 const button = document.createElement('button');
173 button.className = 'btn';
174 button.textContent = 'Fill Template';
175 button.onclick = () => {
176 fillTemplate(instance, sampleData);
177 };
178 return button;
179};
180
181// Reset Document Button
182const resetDocumentButton = (instance) => {
183 const button = document.createElement('button');
184 button.className = 'btn';
185 button.textContent = 'Reset Document';
186 button.onclick = async () => {
187 await instance.Core.documentViewer.loadDocument(defaultDoc, {
188 extension: 'docx',
189 });
190
191 // Reset Sample Data
192 sampleData.bill_to_name = 'Joey Hansen';
193 sampleData.past_due = true;
194 sampleData.pay_by_date = '20-03-2022';
195 sampleData.ship_to_name = 'Kathryn Hoover';
196 sampleData.total_due = '250.00';
197 sampleData.total_owing = '150.00';
198 sampleData.total_paid = '100.00';
199
200 // Reset all text inputs and checkboxes
201 const textInputs = document.querySelectorAll('.text-input');
202 textInputs.forEach((input) => {
203 input.value = sampleData[input.name] || '';
204 });
205 const checkboxes = document.querySelectorAll('.checkbox-input');
206 checkboxes.forEach((checkbox) => {
207 checkbox.checked = sampleData[checkbox.name] || false;
208 });
209 };
210
211 return button;
212};
213
214const createUIControls = (instance) => {
215 // Create a container for all controls (label, dropdown, and buttons)
216 const controlsContainer = document.createElement('div');
217 controlsContainer.className = 'controls-container';
218
219 // Buttons Container
220 const buttonsContainer = document.createElement('div');
221 buttonsContainer.className = 'buttons-container';
222 buttonsContainer.appendChild(fillTemplateButton(instance));
223 buttonsContainer.appendChild(resetDocumentButton(instance));
224 controlsContainer.appendChild(buttonsContainer);
225
226 // Create a container for the text inputs
227 const textInputsContainer = document.createElement('div');
228 textInputsContainer.className = 'text-inputs-container';
229
230 // bill_to_name field
231 const billToNameInput = createTextInput('Bill To name', sampleData.bill_to_name);
232 textInputsContainer.appendChild(billToNameInput);
233
234 // past_due field
235 const pastDueCheckbox = createCheckbox('Past Due', sampleData.past_due);
236 textInputsContainer.appendChild(pastDueCheckbox);
237
238 // pay_by_date field
239 const payByDateInput = createTextInput('Pay By Date', sampleData.pay_by_date);
240 textInputsContainer.appendChild(payByDateInput);
241
242 // ship_to_name field
243 const shipToNameInput = createTextInput('Ship To Name', sampleData.ship_to_name);
244 textInputsContainer.appendChild(shipToNameInput);
245
246 // total_due field
247 const totalDueInput = createTextInput('Total Due', sampleData.total_due);
248 textInputsContainer.appendChild(totalDueInput);
249
250 // total_owing field
251 const totalOwingInput = createTextInput('Total Owing', sampleData.total_owing);
252 textInputsContainer.appendChild(totalOwingInput);
253
254 // total_paid field
255 const totalPaidInput = createTextInput('Total Paid', sampleData.total_paid);
256 textInputsContainer.appendChild(totalPaidInput);
257
258 controlsContainer.appendChild(textInputsContainer);
259 element.insertBefore(controlsContainer, element.firstChild);
260};
261
262
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales