Appearance
Set form data - Vuejs
In this example we set form data in a PDF form. You can setup one-way or two-way data bindings between the form fields in the PDF document and input elements in html.
Prerequisites
This example assumes that there exists a document form.pdf
in your storage. If you do not have such a document you can run the following command to add a document with fields under that name:
shell
node src/post-document.js
Reading the form fields and form field definitions
In the handler for the loaded event you can retrieve the field definitions and form data.
ts
<script setup lang="ts">
import { ref, onMounted, nextTick } from "vue";
import {
PdfApplication,
PdfDocument,
PdfPages,
type IPdfDocument,
type PdfDocumentCustomEvent,
type IPdfField,
} from "@tallcomponents/unopdf-vue";
import PdfInput from "./PdfInput.vue";
// Note: this example uses Vite and retrieves the keys from the environment.
// This should not be used in production.
// See the authentication section in the usage guide for more information.
const publickey = import.meta.env.VITE_PUBLIC_KEY;
const privatekey = import.meta.env.VITE_PRIVATE_KEY;
const app = ref<{ $el: HTMLPdfApplicationElement }>();
const document = ref<{ $el: HTMLPdfDocumentElement }>();
const pages = ref<{ $el: HTMLPdfPagesElement }>();
const formDocument = ref<IPdfDocument>();
const fieldDefinitions = ref<IPdfField[] | null | undefined>([]);
const formData = ref<Record<string, string>>({});
onMounted(async () => {
await nextTick();
const _app = app.value?.$el;
if (_app) {
formDocument.value = (await _app.getDocuments()).find((doc) => doc.originalFileName == "form.pdf");
const _doc = document.value?.$el;
if (_doc && formDocument.value && formDocument.value.id) {
await _doc.open(formDocument.value.id);
}
}
});
// #region on-loaded
const onLoaded = async ({ target }: PdfDocumentCustomEvent<string | null>) => {
fieldDefinitions.value = await target.getFields();
formData.value = await target.getFormData();
};
// #endregion on-loaded
const onChanged = (value: Record<string,string>) => {
// store form data
const _pdf = document.value?.$el as HTMLPdfDocumentElement;
_pdf.setFormData(value);
formData.value = value;
}
// #region set-form-data-on-change
const onFormDataChanged = async ({ target }: PdfDocumentCustomEvent<unknown>) => {
formData.value = await target.getFormData();
};
// #endregion set-form-data-on-change
// #region save-document-handler
const saveDocument = async () => {
const _doc = document.value?.$el;
if (_doc) {
await _doc.save();
const responseDocument = await _doc.save();
if (responseDocument) {
console.log(`document saved with id ${responseDocument.id}`);
} else {
console.log(`document not saved`);
}
console.log('form data', (await _doc.getFormData()));
}
}
// #endregion save-document-handler
const downloadDocument = () => {
document.value?.$el.download()
}
</script>
<template>
<h1>UnoPdf Example: Fill Form Fields in Document from HTML input fields in Vue</h1>
<PdfApplication
ref="app"
:publickey="publickey"
:privatekey="privatekey"
/>
<!-- #region pdf-document -->
<PdfDocument
ref="document"
@loaded="onLoaded"
@formdatachanged="onFormDataChanged"
/>
<!-- #endregion pdf-document -->
<div style="display: flex">
<div>
<PdfPages ref="pages" />
</div>
<div>
<div style="padding: 10px">
You can fill out the fields in the document and see your changes reflected below
</div>
<div style="padding: 10px">
<h2>
Fields
</h2>
<div
v-for="(value,name) in formData"
:key="name"
style="padding-top:10px"
>
<div>{{ name }}: {{ value }}</div>
<PdfInput
:form-data="formData"
:field-definitions="fieldDefinitions!"
:name="name"
@change="onChanged"
/>
</div>
</div>
<div style="display: flex">
<div style="padding: 10px">
<!-- #region save-document-button -->
<button
type="button"
style="font-size: 20px"
@click="saveDocument"
>
save
</button>
<!-- #endregion save-document-button -->
</div>
<div style="padding: 10px">
<button
type="button"
style="font-size: 20px"
@click="downloadDocument"
>
download
</button>
</div>
</div>
</div>
</div>
</template>
When you want to show an input element in your html for a PDF field you can retrieve the field type from the field definition:
ts
const fieldType = computed(() => fieldDefinition.value?.fieldType)
Depending on the field type there are other fields available in the field definition such as checkBoxOnValue
, checkBoxOffValue
, listOptions
and radioButtonOptions
ts
const radioButtonOptions = computed(() => fieldDefinition.value?.radioButtonOptions)
const listOptions = computed(() => fieldDefinition.value?.listOptions)
const checkboxOnValue = computed(() => fieldDefinition.value?.checkBoxOnValue || 'On');
const checkboxOffValue = computed(() => fieldDefinition.value?.checkBoxOffValue || 'Off');
By combining this information you can dynamically determine which type of HTML input to use.
html
<div v-if="fieldType == 'RadioButtonField'">
<template
v-for="option in radioButtonOptions"
:key="option"
>
<input
v-model="value"
type="radio"
:value="option"
@input="event => setValue((event.target as HTMLInputElement).value)"
> {{ option }}
</template>
</div>
<div v-else-if="fieldType == 'TextField'">
<input
type="text"
:value="value"
@input="event => setValue((event.target as HTMLInputElement).value)"
>
</div>
<div v-else-if="fieldType == 'ListBoxField' || fieldType == 'DropDownListField'">
<select
v-model="value"
:name="name"
:size="fieldType == 'ListBoxField' ? listOptions?.length || 0 : undefined"
@change="event => setValue((event.target as HTMLSelectElement).value)"
>
<template
v-for="option in listOptions"
:key="option.export"
>
<option
:value="option.export"
>
{{ option.display }}
</option>
</template>
</select>
</div>
<div v-else-if="fieldType == 'CheckBoxField'">
<input
type="checkbox"
:value="value"
:checked="checked"
@input="onChange"
>
</div>
Screenshot
Code
vue
<script setup lang="ts">
import { ref, onMounted, nextTick } from "vue";
import {
PdfApplication,
PdfDocument,
PdfPages,
type IPdfDocument,
type PdfDocumentCustomEvent,
type IPdfField,
} from "@tallcomponents/unopdf-vue";
import PdfInput from "./PdfInput.vue";
// Note: this example uses Vite and retrieves the keys from the environment.
// This should not be used in production.
// See the authentication section in the usage guide for more information.
const publickey = import.meta.env.VITE_PUBLIC_KEY;
const privatekey = import.meta.env.VITE_PRIVATE_KEY;
const app = ref<{ $el: HTMLPdfApplicationElement }>();
const document = ref<{ $el: HTMLPdfDocumentElement }>();
const pages = ref<{ $el: HTMLPdfPagesElement }>();
const formDocument = ref<IPdfDocument>();
const fieldDefinitions = ref<IPdfField[] | null | undefined>([]);
const formData = ref<Record<string, string>>({});
onMounted(async () => {
await nextTick();
const _app = app.value?.$el;
if (_app) {
formDocument.value = (await _app.getDocuments()).find((doc) => doc.originalFileName == "form.pdf");
const _doc = document.value?.$el;
if (_doc && formDocument.value && formDocument.value.id) {
await _doc.open(formDocument.value.id);
}
}
});
// #region on-loaded
const onLoaded = async ({ target }: PdfDocumentCustomEvent<string | null>) => {
fieldDefinitions.value = await target.getFields();
formData.value = await target.getFormData();
};
// #endregion on-loaded
const onChanged = (value: Record<string,string>) => {
// store form data
const _pdf = document.value?.$el as HTMLPdfDocumentElement;
_pdf.setFormData(value);
formData.value = value;
}
// #region set-form-data-on-change
const onFormDataChanged = async ({ target }: PdfDocumentCustomEvent<unknown>) => {
formData.value = await target.getFormData();
};
// #endregion set-form-data-on-change
// #region save-document-handler
const saveDocument = async () => {
const _doc = document.value?.$el;
if (_doc) {
await _doc.save();
const responseDocument = await _doc.save();
if (responseDocument) {
console.log(`document saved with id ${responseDocument.id}`);
} else {
console.log(`document not saved`);
}
console.log('form data', (await _doc.getFormData()));
}
}
// #endregion save-document-handler
const downloadDocument = () => {
document.value?.$el.download()
}
</script>
<template>
<h1>UnoPdf Example: Fill Form Fields in Document from HTML input fields in Vue</h1>
<PdfApplication
ref="app"
:publickey="publickey"
:privatekey="privatekey"
/>
<!-- #region pdf-document -->
<PdfDocument
ref="document"
@loaded="onLoaded"
@formdatachanged="onFormDataChanged"
/>
<!-- #endregion pdf-document -->
<div style="display: flex">
<div>
<PdfPages ref="pages" />
</div>
<div>
<div style="padding: 10px">
You can fill out the fields in the document and see your changes reflected below
</div>
<div style="padding: 10px">
<h2>
Fields
</h2>
<div
v-for="(value,name) in formData"
:key="name"
style="padding-top:10px"
>
<div>{{ name }}: {{ value }}</div>
<PdfInput
:form-data="formData"
:field-definitions="fieldDefinitions!"
:name="name"
@change="onChanged"
/>
</div>
</div>
<div style="display: flex">
<div style="padding: 10px">
<!-- #region save-document-button -->
<button
type="button"
style="font-size: 20px"
@click="saveDocument"
>
save
</button>
<!-- #endregion save-document-button -->
</div>
<div style="padding: 10px">
<button
type="button"
style="font-size: 20px"
@click="downloadDocument"
>
download
</button>
</div>
</div>
</div>
</div>
</template>
vue
<script setup lang="ts">
import type { IPdfField } from '@tallcomponents/unopdf-core';
import { computed } from 'vue';
interface Props {
name: string;
fieldDefinitions: IPdfField[];
formData: Record<string, string>
}
const props = defineProps<Props>();
const emit = defineEmits<{ change: [Record<string, string>] }>()
if (!Object.keys(props.formData).includes(props.name)) {
throw new Error(`unknown form field ${props.name}`)
}
const fieldDefinition = computed(() => props.fieldDefinitions.find((f) => f.fullName === props.name));
if (!fieldDefinition.value) {
throw new Error(`no field definition for ${props.name}`);
}
// #region field-type
const fieldType = computed(() => fieldDefinition.value?.fieldType)
// #endregion field-type
// #region read-field-options
const radioButtonOptions = computed(() => fieldDefinition.value?.radioButtonOptions)
const listOptions = computed(() => fieldDefinition.value?.listOptions)
const checkboxOnValue = computed(() => fieldDefinition.value?.checkBoxOnValue || 'On');
const checkboxOffValue = computed(() => fieldDefinition.value?.checkBoxOffValue || 'Off');
// #endregion read-field-options
const setValue =
(value: string) => {
const f = { ...props.formData, [props.name]: value };
emit('change', f);
}
const value = computed(() => props.formData[props.name]);
const checked = computed(() => value.value === checkboxOnValue.value)
const onChange = () => {
if (props.formData[props.name] === checkboxOnValue.value) {
setValue(checkboxOffValue.value)
} else {
setValue(checkboxOnValue.value)
}
}
</script>
<template>
<!-- #region input-field -->
<div v-if="fieldType == 'RadioButtonField'">
<template
v-for="option in radioButtonOptions"
:key="option"
>
<input
v-model="value"
type="radio"
:value="option"
@input="event => setValue((event.target as HTMLInputElement).value)"
> {{ option }}
</template>
</div>
<div v-else-if="fieldType == 'TextField'">
<input
type="text"
:value="value"
@input="event => setValue((event.target as HTMLInputElement).value)"
>
</div>
<div v-else-if="fieldType == 'ListBoxField' || fieldType == 'DropDownListField'">
<select
v-model="value"
:name="name"
:size="fieldType == 'ListBoxField' ? listOptions?.length || 0 : undefined"
@change="event => setValue((event.target as HTMLSelectElement).value)"
>
<template
v-for="option in listOptions"
:key="option.export"
>
<option
:value="option.export"
>
{{ option.display }}
</option>
</template>
</select>
</div>
<div v-else-if="fieldType == 'CheckBoxField'">
<input
type="checkbox"
:value="value"
:checked="checked"
@input="onChange"
>
</div>
<!-- #endregion input-field -->
<div v-else>
Unsupported Field Type: {{ fieldType }}
</div>
</template>
Running the example
- Download the vue - Set form data project
- Unzip the file to a directory
vue-set-form-data
.shellunzip vue-set-form-data.zip -d vue-set-form-data
- Open a terminal and go to that directoryshell
cd vue-set-form-data
- Install dependenciesshell
npm install
shellyarn
- Start the projectshell
npm run start
shellyarn start