Reputation: 10918
How can I use a vendor libary (specifically I want to use PDF.js) in a Vue component? (I only want to load it for this specific component as they are rather larger files)
I'm building an editor that needs to load a pdf. So I placed the pdf.js and pdf.worker.js in /src/assets/vendor/pdfjs
Then I load both in the template-editor-page.hbs that also loads the component:
<div class="content">
<div class="row fill">
<div class="col-md-2 fill br pt30">
</div>
<div class="col-md-10 fill pt30 pl30 pr30">
<div id="template-editor" class="template-editor">
<template-editor template-src="{{template.src}}"></template-editor>
</div>
</div>
</div>
</div>
<script src="/assets/js/template-editor.bundle.js"></script>
<script src="/assets/vendor/pdfjs/pdf.js"></script>
<script src="/assets/vendor/pdfjs/pdf.worker.js"></script>
my template-editor.js (do I have to load it here?):
import Vue from 'vue';
import templateEditor from './components/template-editor.vue';
new Vue({
el: '#template-editor',
components: { templateEditor }
});
Now I want to load the file in my template-editor.vue:
<template>
<!-- ... -->
</template>
<script>
export default {
props: ['templateSrc'],
name: 'template-editor',
data() {
return {
src: this.templateSrc
};
},
methods: {
render() {
PDFJS.getDocument(this.$data.src).then(function(pdf) {
console.log(pdf);
}, err => console.log(err));
}
},
created: function() {
this.render();
}
};
</script>
But I get an error saying
ReferenceError: PDFJS is not defined
Everything else seems to be working out fine. What am I missing?
Upvotes: 2
Views: 7803
Reputation: 3927
I have an old project with Webpack 4, Babel and Vue 2. It has been painful to use the pdf.js library from Mozilla.
I ended to create a private Vue Component called 'pdf-viewer':
<template>
<div ref="pdfrender" class="pdfrender"></div>
</template>
<script>
export default {
emits:['rendered'],
props:{
libSrc:{ // the pdf.js version to use
type:String,
default:"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.3.122/pdf.min.js"
},
pdfData:{ // a format supportd by pdf.js with "getDocument()" function – e.g. a buffer
required:true,
type:null
},
viewPortOptions:{ // the options to pass to `page.getViewport()`
type:Object,
default:() => ({scale:1})
}
},
data () {
return {
pdfjsLib:null
}
},
methods:{
renderPage (page, num) {
return new Promise(pr => {
let viewport = page.getViewport(this.viewPortOptions);
let wrapper = document.createElement("div");
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let renderContext = {
canvasContext: ctx,
viewport: viewport
};
canvas.height = viewport.height;
canvas.width = viewport.width;
wrapper.appendChild(canvas);
this.$refs.pdfrender.appendChild(wrapper);
page.render(renderContext).promise.then(() => {
console.info(`[pdf-viewer] Page #${num} rendered.`);
pr();
});
})
},
renderPages (pdfDoc) {
let arrProm = [];
for(let num = 1; num <= pdfDoc.numPages; num++) arrProm.push(pdfDoc.getPage(num).then(page => this.renderPage(page, num)));
Promise.all(arrProm)
.then(() => {
console.info("[pdf-viewer] All pages rendered.");
// inform the parent when the PDF has rendered
this.$emit('rendered');
})
},
renderPDF() {
this.pdfjsLib.getDocument(this.pdfData).promise.then(this.renderPages);
},
waitForPdfLib () {
// we wait for the library to be loaded in the browser
return new Promise(pr => {
if (!window['pdfjs-dist/build/pdf']) {
setTimeout(() => this.waitForPdfLib().then(pr), 1000);
} else {
this.pdfjsLib=window['pdfjs-dist/build/pdf'];
this.pdfjsLib.GlobalWorkerOptions.workerSrc = this.libSrc.replace(/pdf.(min.)?js$/, "pdf.worker.$1js");
pr();
}
})
}
},
created () {
// make sure the PDF.js is not loaded yet
if (!window['pdfjs-dist/build/pdf']) {
let tag = document.head.querySelector(`[src="${this.libSrc}"]`);
if (!tag) {
tag = document.createElement("script");
tag.setAttribute("src", this.libSrc);
tag.setAttribute("type", "text/javascript");
document.head.appendChild(tag);
}
}
this.waitForPdfLib()
.then(() => this.renderPDF())
}
}
</script>
<style>
.pdfrender {
overflow: auto;
height: calc(100vh - 250px);
width: 850px;
}
.pdfrender > div {
text-align: center;
background-color: #EEE;
}
</style>
Then to call it:
<template>
<div id="app">
<div v-if="!pdfRendered">Loading the PDF...</div>
<pdf-viewer :pdf-data="pdfData" @rendered="pdfRendered=true" v-if="pdfData"></pdf-viewer>
</div>
<template>
<script>
import PDFComponent from './pdf-viewer.vue'
export default {
components:{
'pdf-viewer':PDFComponent
},
data () {
return {
pdfData:'',
pdfRendered:false
}
},
created () {
this.pdfData="whatever format supported by getDocument()"; // see https://github.com/mozilla/pdf.js/blob/3f33fbf8cf1d7eb5f29de32288ebaa4dd4922501/src/display/api.js#L242
}
}
</script>
Upvotes: 1
Reputation: 10918
Thank's for your help guys. Turns out the answer was hidden in the first snippet: I import the pdfjs AFTER the bundle. But since the bundle needs it, I need to import it BEFORE:
<script src="/assets/vendor/pdfjs/pdf.js"></script>
<script src="/assets/vendor/pdfjs/pdf.worker.js"></script>
<script src="/assets/js/template-editor.bundle.js"></script>
Really not all that complicated after all ;)
Upvotes: -1
Reputation: 1912
Instead of script
tags for your vendor scripts, better use webpacks dynamic import feature (https://webpack.js.org/guides/code-splitting/#dynamic-imports) to load this vendor library in your render function:
render() {
import('/assets/vendor/pdfjs/pdf.js').then(PDFJS => {
PDFJS.getDocument(this.$data.src).then(function(pdf) {
console.log(pdf);
}, err => console.log(err));
}
}
For import
to work you will also have to install this babel plugin http://babeljs.io/docs/plugins/syntax-dynamic-import/.
Upvotes: 1
Reputation: 23483
I think all that's missing is an import statement in your component,
CORRECTION Try with an '@' in the import location below. I forgot, your component is probably in a sub-folder of 'src'. Also see note below about pdfjs-dist.
<script>
import { PDFJS } from '@/assets/vendor/pdfjs/pdf.js'
export default {
props: ['templateSrc'],
name: 'template-editor',
...
Alternative
Since you have webpack, you might be better off installing pdfjs-dist into node modules (see pdfjs-dist), and removing it from './assets/vendor/pdfjs/pdj.js'
npm install pdfjs-dist
If you do this, the import is more 'standard',
import { PDFJS } from 'pdfjs-dist'
Upvotes: 3