Utilize Vue with a unique library (pdf.js) within a component

Can anyone provide guidance on incorporating a vendor library (specifically PDF.js) into a Vue component? I only need to load it for this specific component due to the large file size.

I am working on an editor that requires loading a PDF. I have placed pdf.js and pdf.worker.js in the /src/assets/vendor/pdfjs directory.

Next, I load both files in the template-editor-page.hbs which 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>

In my template-editor.js file (do I need 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 use 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>

However, I encounter an error stating

ReferenceError: PDFJS is not defined

All other aspects are functioning correctly. What could be causing this issue?

Answer №1

One key element that seems to be missing is an import statement within your component,

CORRECTION Make sure to include an '@' symbol in the import path below. It's easy to overlook, but keep in mind that your component may reside within a sub-folder of 'src'. Also, don't forget to check out the note regarding pdfjs-dist.

<script>
  import { PDFJS } from '@/assets/vendor/pdfjs/pdf.js'

  export default {
    props: ['templateSrc'],
    name: 'template-editor',
    ...

Alternative

If you're using webpack, consider installing pdfjs-dist into node modules (refer to pdfjs-dist), and then removing it from './assets/vendor/pdfjs/pdj.js'

npm install pdfjs-dist

By doing this, the import process becomes more streamlined and 'standard',

import { PDFJS } from 'pdfjs-dist'

Answer №2

Utilize webpacks dynamic import feature instead of using script tags for your vendor scripts. This allows you to load the vendor library in your render function more efficiently:

render() {
    import('/assets/vendor/pdfjs/pdf.js').then(PDFJS => {
        PDFJS.getDocument(this.$data.src).then(function(pdf) {
          console.log(pdf);
        }, err => console.log(err));
    }
}

In order for the import to work properly, make sure to install the babel plugin mentioned in this link: http://babeljs.io/docs/plugins/syntax-dynamic-import/.

Answer №3

Dealing with the pdf.js library from Mozilla in an old project using Webpack 4, Babel, and Vue 2 has been quite a challenge.

To tackle this issue, I came up with a custom Vue Component named '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."); 
        this.$emit('rendered'); 
      }) 
    }, 

    renderPDF() { 
      this.pdfjsLib.getDocument(this.pdfData).promise.then(this.renderPages); 
    }, 

    waitForPdfLib () { 
      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 () {
    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> 

Now, to utilize this component:

<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()"; 
  } 
}
</script> 

Answer №4

Appreciate your assistance everyone. Interestingly, the solution was right there in the initial code snippet: I mistakenly imported the pdfjs files after the bundle. However, since the bundle depends on them, I should have imported them beforehand:

<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>

It's actually quite straightforward in the end ;)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to transfer a JavaScript variable to a JSON format?

Hey there! I'm diving into the world of coding and eager to learn. Recently, I successfully passed a query parameter into a JavaScript variable using the following code: const queryString = window.location.search; console.log(queryString); const urlPa ...

Material-UI organizes its Grid Items vertically, creating a column layout rather than a horizontal row

I'm struggling to create a grid with 3 items in each row, but my current grid layout only displays one item per row. Each grid item also contains an image. How can I modify the code to achieve a grid with 3 items in a row? Here's the code snippet ...

Vue-i18n does not offer a default export option

Hello everyone! This is my first experience using vue-i18n in a project with TypeScript + Vue. Following the instructions from the official site, I installed it using yarn install vue-i18n. Next, I tried to import it into main.ts using import VueI18n from ...

Creating an array based on specific conditions being satisfied (Using Javascript)

I am struggling with a coding problem involving adding users to groups. The goal is to add each user to a group array, with a maximum of 3 users per group. If a group reaches 3 users, it should be moved to another array that collects all the groups. I then ...

Can a javascript file be included through a symbolic link?

I am working on a PHP web application and I have a question: Is it advisable to store my JavaScript files in the private directory? If yes, can I include them from a symbolic link located in the public directory? ...

Is it possible to retrieve the complete HTTP response text using Node.js from a HTTP module's .get response?

I have a straightforward web server set up: const ws = require('http'); ws.createServer( function(req,res) { console.log('request received'); res.write('Hello world'); res.end(); }) ...

Switch image formats to webp using react js

Utilizing the react-dropzone package, I aim to incorporate images into my application. To achieve this, I must send the images to a firebase server after managing image conversions. Whilst browsing through YouTube tutorials, I came across a guide demonstr ...

The issue of not being able to add data to the client is encountered in a local setup involving Socket.io, MSSQL

I have a large dataset that needs to be retrieved from a SQL server using Node.js and then broadcasted to clients in real-time using Socket.IO. I've been successful in fetching the data, but the UI on the client side isn't updating. Although I c ...

The submission of data on a PHP/HTML form resulted in a 405 HTTP Error

Currently, I am in the process of constructing a form where the entered information will be forwarded to an email address. My tools for this task are HTML and PHP. Unfortunately, upon clicking the SUBMIT button, I encounter the ensuing issue: 405 - HTTP ...

ESLint throws an error when the watch method in Vue is used with the immediate option

My Vue code includes a Watch method with immediate: true watch: { currentQuestion() { // console.log("Watch currentQuestion", "Start"); immediate: true; this.selectedIndex = null; this.shuffleAnswers(); } } Upon running ...

Encountering difficulties reaching $refs within component method

Trying to access a ref defined within a template when an element is clicked. Here's the HTML: <!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protectio ...

Utilizing Angular 1.5 and ES6 to Enhance Dependency Injection

Hi there, I am currently exploring Angular and attempting to integrate ES6 into my workflow. I seem to be facing an issue with dependency injection where I cannot seem to get it working as expected. Here is a snippet from my index.js file: import ...

The function driver.switchTo.frame() does not exist

I am a beginner with Selenium and facing an issue with a task I need to accomplish: Go to https://pastebin.com Paste "Hello from WebDriver" Set the paste expiration to 10 Minutes //Struggling with this step Set the paste title as "helloweb" I am using Ja ...

What is the best way to fill in fields on my webpage using a dropdown menu choice?

I'm exploring the world of ASP.NET MVC, AJAX, and jQuery for the first time. I'm attempting to populate some text boxes on my website based on a dropdown selection but it seems like the data isn't getting through. I suspect that the 'ch ...

Tips for showcasing several images with accompanying content once the webpage has finished loading

I am faced with an issue on my PHP website. The website is a social networking platform with numerous images and contents. I am seeking a way to first display only the content to the user, then show the images after they have all finished loading. Addition ...

What is the best way to completely manipulate the rotation of a <div>?

Does anyone have a solution for controlling the rotation of a div using CSS? I've been grappling with this issue for some time now, but I can't seem to find a proper fix. Whenever I try to rotate a div within a table cell, the width of the cell ...

Leveraging the withWidth higher order component (HOC) provided by

I am currently using version 3.9.2 of Material UI and experimenting with the withWidth HOC in a server-side rendered application. When I disable Javascript in the debugger options of Chrome Developer Tools, everything functions as expected by displaying t ...

What are some solutions for ensuring that the dots on a slideshow function automatically?

My attempt to showcase an automated slideshow seems to be partially successful. The images are changing as intended, but the dots below aren't functioning properly. Although clicking on a dot triggers the desired action, I am aiming for all dots to tu ...

Tips for displaying an uploaded image using the Valums file uploader

I recently implemented the Andrew Valums File Uploader on my website and it's functioning as expected. I am now looking to modify it so that instead of displaying just the uploaded filename and size, it will show the uploaded picture directly in the b ...

React displaying incorrect data during array iteration

I have encountered a peculiar problem that I can't seem to find any information about online. If my issue has already been discussed elsewhere, I apologize for the duplicated query. The challenge revolves around managing a "Schedule" represented by a ...