How to import Mozilla PDF.js in Vue project? - javascript

The question is simple. How do I correctly import the PDF.js library into a Vuejs project?
The library is undefined when I log it.
See my problem in a codesandbox live here.
This is how I am trying it now:
<script>
import pdfjsLib from "pdfjs-dist/build/pdf";
// import { PDFViewer } from "pdfjs-dist/web/pdf_viewer";
import "pdfjs-dist/web/pdf_viewer.css";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://cdn.jsdelivr.net/npm/pdfjs-dist#2.0.943/build/pdf.worker.min.js";
export default {
name: "PdfViewer",
mounted() {
pdfjsLib.getDocument("./sample.pdf").then((doc) => {
console.log("doc: ", doc);
});
},
methods: {},
};
</script>
But that gives me the following error: Cannot read property 'GlobalWorkerOptions' of undefined

I think the error occurs if pdfjsLib does not fall into the global scope
, see also codesandbox :
<template>
<div id="pageContainer">
<div id="viewer" class="pdfViewer"></div>
</div>
</template>
<script>
import pdfjsLib from "pdfjs-dist/build/pdf";
import { PDFViewer } from "pdfjs-dist/web/pdf_viewer";
import "pdfjs-dist/web/pdf_viewer.css";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://cdn.jsdelivr.net/npm/pdfjs-dist#2.0.943/build/pdf.worker.min.js";
export default {
name: "PdfViewer",
props: { docPath: String },
mounted() {
this.getPdf();
},
methods: {
async getPdf() {
let container = document.getElementById("pageContainer");
let pdfViewer = new PDFViewer({
container: container,
});
let pdf = await pdfjsLib.getDocument(this.docPath);
pdfViewer.setDocument(pdf);
},
},
};
</script>
<style>
#pageContainer {
margin: auto;
width: 80%;
}
div.page {
display: inline-block;
}
</style>
use it:
<PdfViewer docPath="./sample.pdf" />

In case anyone else needs it, the soution is really simple. You just have to import it like this:
import * as pdfjsLib from "pdfjs-dist/build/pdf";

Pdf.js provide a solution for us. Webpack.js included in the project.
const pdfjsLib = require("pdfjs-dist/webpack");
If you get an error like below:
./node_modules/pdfjs-dist/build/pdf.min.js 22:36927
Module parse failed: Unexpected token (22:36927)
Then we have to use es5/build/pdf.js, so we can create src/pdfjs-webpack.js :
"use strict";
var pdfjs = require("pdfjs-dist/es5/build/pdf.min.js");
var PdfjsWorker = require("worker-loader?esModule=false&filename=[name].js!pdfjs-dist/es5/build/pdf.worker.min.js");
if (typeof window !== "undefined" && "Worker" in window) {
pdfjs.GlobalWorkerOptions.workerPort = new PdfjsWorker();
}
module.exports = pdfjs;
then:
const pdfjsLib = require("../pdfjs-webpack");

vue-cli5 already use webpack5, and webpack5 has a built-in web worker and is very easy to use.
Create a file: pdfjs-webpack5.js
import * as pdfjsLib from 'pdfjs-dist'
pdfjsLib.GlobalWorkerOptions.workerPort = new Worker(new URL('pdfjs-dist/build/pdf.worker.js', import.meta.url))
export default pdfjsLib
According to the example getinfo.js given in Setup PDF.js in a website, you can easily read the contents of PDF files.
I use the version of the package.
pdfjs-dist: 2.15.349
webpack: 5.74.0
#vue/cli*: 5.0.8

Related

How to use composable inside a component from the same npm module

I want to create a npm plugin that contains all the elements that I often use in my Nuxt projects.
I followed the instructions here : https://v3.nuxtjs.org/guide/going-further/modules/ and there : https://www.youtube.com/watch?v=7MBOOxUg1yM but I'm still experiencing an issue with the composables.
I added this in the setup function of my module.js :
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url));
addImports({
name: 'useTheme',
as: 'useTheme',
from: resolve(runtimeDir, 'composables')
});
await addComponentsDir({
path: resolve(runtimeDir, 'components'),
prefix: 'my-project'
});
And here's my composables.js :
import { useState } from '#app';
export const useTheme = () => useState('theme', () => 'dark');
And when I try to use the useTheme in my ThemeToggle.vue, I have an error "useTheme is not defined"
<script setup>
const theme = useTheme();
</script>
I tried to add an import like this :
import { useTheme } from '#app';
But it throw me an other error __vite_ssr_import_0__.useTheme is not a function
Any idea ?

Vue 3: Add global method via Plugin not working

I'm trying to build a method that can be used inside any template to automatically build local image urls.
The issue I'm facing is that when I try to build a plugin that adds a global property, it's not working!
Plugin code
// src/plugins/urlbuilder.js
export default {
install: (app) => {
app.config.globalProperties.buildImageUrl = imageName => require('#/assets/images/' + imageName);
}
}
Main.js file
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import urlbuilder from './plugins/urlbuilder.js'
createApp(App).use(router).use(urlbuilder).mount('#app')
Home view where I render the image
// src/views/Home.vue
<template>
<img :src="buildImageUrl('myimage.jpg')">
</template>
and I'm getting this error in my the dev console:
Uncaught (in promise) TypeError: _ctx.buildImageUrl is not a function
at Proxy.render (cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader-v16/dist/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader-v16/dist/index.js?!./src/views/Home.vue?vue&type=template&id=fae5bece&scoped=true:57)
at renderComponentRoot (runtime-core.esm-bundler.js:922)
at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js:4667)
at ReactiveEffect.run (reactivity.esm-bundler.js:195)
at setupRenderEffect (runtime-core.esm-bundler.js:4793)
at mountComponent (runtime-core.esm-bundler.js:4576)
at processComponent (runtime-core.esm-bundler.js:4534)
at patch (runtime-core.esm-bundler.js:4138)
at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js:4744)
at ReactiveEffect.run (reactivity.esm-bundler.js:195)
Note: This works when I add purely a global property, but I read the best way to do this was via plugin.
It works when I do this:
app = createApp(App)
app.config.globalProperties.buildImageUrl = imageName => require('#/assets/images/' + imageName)
app.use(router).mount('#app')
What am I doing wrong?
A better way would be to use provide and inject
import urlbuilder from './plugins/urlbuilder.js'
app.provide('$urlbuilder', urlbuilder);
Read more about provide and inject
You should get global properties by this:
const instance = getCurrentInstance()
const globalProperties = instance.appContext.config.globalProperties
console.log(globalProperties)
Recommand use provide and inject.
Or use a hook:
useGlobalProps.ts
import { getCurrentInstance } from 'vue'
import type { ComponentInternalInstance } from 'vue'
function useGlobalProps() {
const { appContext } = getCurrentInstance() as ComponentInternalInstance
const globalProps = appContext.config.globalProperties
return { ...globalProps }
}
export default useGlobalProps
use it in component:
import useGlobalProps from '#/hooks/useGlobalProps'
const { testFn, globalFn } = useGlobalProps()
testFn()
globalFn('global function in main.js')
Register globalFn in main.js
app.config.globalProperties.globalFn = function testGlobal(name: string) {
console.log(name)
}
Register testFn by plugin:
myPlugin.js
export default function (app: App<HTMLElement>) {
app.config.globalProperties.testFn = () => {
console.log('install global properties')
}
return app
}
use plugin in main.js
import myPlugin from './myPlugin.js'
// ...
app.use(myPlugin)

Bpmn io vue 3 integration problems

Im currently trying to embed bpmn io in a vue 3 application. Im able to load the diagram using the raw loader in webpack. Unfortunately there are some other issues.
1. The side bar on the left is not appearing
2. The canvas.zoom is not working. Diagram occupies only a small portion of the screen width and height.
MainPage.vue (file where bpmn magic resides)
<template>
<div ref="container" id="canvas" style="height: 100%"/>
</template>
<script>
import pizzaDiagram from '../assets/pizza-diagram.bpmn';
export default {
name: 'main-page',
mounted() {
this.$nextTick(() => {
const container = this.$refs.container;
let modeler = this.$bpmnModeler;
modeler.attachTo(container)
modeler.options = {
container,
height: "100%",
width: "100%"
}
modeler.importXML(pizzaDiagram).then((result) => {
const {warnings} = result;
console.log('success !', warnings);
const canvas = modeler.get('canvas');
canvas.zoom('fit-viewport')
}).catch((err) => {
const {warnings, message} = err;
console.trace('something went wrong. what went wrong :', warnings, message)
})
})
},
data() {
return {}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#canvas{
height: 100%;
width: 100%;
}
</style>
main.js (file where i register my bpmn components for general use app-wide)
import { createApp } from 'vue'
import App from './App.vue'
import BpmnJS from 'bpmn-js/dist/bpmn-navigated-viewer.production.min.js'
import BpmnModeler from "bpmn-js";
import BpmnViewer from "bpmn-js";
const app = createApp(App);
app.config.globalProperties.$bpmnViewer = new BpmnViewer();
app.config.globalProperties.$bpmnModeler = new BpmnModeler();
app.config.globalProperties.$bpmnInstance = new BpmnJS();
app.mount('#app')
I was able to solve this. The problem was that i was using the ref=container on the template and then passing it to the canvas options. When i used `document.getElementById("container") to refer to the element i was able to get it to work.

Using Vuetify with i18n using CDNs only

I'm working on a Vue project on a static environment with no Node or Vue-cli,
We're importing Vue, Vuetify and vue-i18n using CDNs
We need to have the Vuetify components translated using the Vue-i18n like shown here
Here is a codepen of an attempt i've made, trying to translate the pagination part at the bottom.
I've tried using Vue.use() but couldn't get it to work, no errors in the console and no translation on the page.
import App from '../components/App.vue.js';
import i18n from '../lang/languages.js';
import store from './store/store.js';
Vue.filter('toUpperCase', function(value) {
return value.toUpperCase();
});
Vue.config.devtools = true;
Vue.use(Vuetify, {
lang: {
t: (key, ...params) => i18n.t(key, params)
}
});
new Vue({
i18n,
store,
el: '#app',
render: (h) => h(App)
});
lang/languages.js:
import { russian } from './languages/russian.js';
import { chineseSimple } from './languages/chinese-simple.js';
import { german } from './languages/german.js';
import { portuguese} from './languages/portuguese.js';
const languages = {
'ru': russian,
'zh-Hans': chineseSimple,
'de': german,
'pt': portuguese,
};
const i18n = new VueI18n({
locale: 'en',
messages: languages
});
export default i18n;
What you are looking for is not available in CDN distributions. You might ask why?
Look at this code:
const Vuetify: VuetifyPlugin = {
install (Vue: VueConstructor, args?: VuetifyUseOptions): void {
Vue.use(VuetifyComponent, {
components,
directives,
...args
})
},
version: __VUETIFY_VERSION__
}
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(Vuetify)
}
Especially this part:
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(Vuetify)
}
It automatically installs the Vuetify without any configurations such as language and etc, so your Vue.use(Vuetify, {..}) won't get called because Vue won't install plugins twice!
What you could do?
Clone the Vuetify repo and change this part of the code and build a new dist for your self.
Save as the file vuetify.dist.js and change that part of the code
Use some hacky tricky workarounds which I don't recommend, but I created a sample for you.
Here is a code pen example, What I actually do:
Load Vue.js file with scripts tag
Use fetch api to download content of vuetify.dist.min.js
Replace window.Vue to something else to make sure vuetify won't install itself automatically!
Eval the changed code
I DON'T RECOMMEND THIS APPROACH AT ALL
fetch("https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.5.14/vuetify.min.js")
.then(res => res.text())
.then(vutify => {
eval(vutify.replace("window.Vue", "window.Vue2"));
Vue.use(Vuetify, {
lang: {
t: (key, ...params) => i18n.t(key, params)
}
});
const App = Vue.component("app", {
template: `

VueJS Component import failed

I have a simple demo I wanna try out to learn more about VueJS components. But when I load my page, I receive the error: Unexpected Token Import, in this line
import GISView from './components/GISView.vue';
when I remove this, GISView is not defined. I use Laravel 5.4 and webpack for compiling the scripts. Why is the component not found?
Main.js
import GISView from './components/GISView.vue';
window.Vue = Vue;
window.Event = new class {
constructor() {
this.Vue = new Vue();
}
fire(event, data = null) {
this.Vue.$emit(event, data);
}
listen(event, callback) {
this.Vue.$on(event, callback);
}
};
window.app = new Vue({
el: '#app',
components: {
GISView: GISView
},
data: {
},
methods: {
init: function() {
this.$broadcast('MapsApiLoaded');
}
}
});
GISView.vue
<script>
import GoogleMaps from '../mixins/GoogleMaps.js';
export default {
mixins: [GoogleMaps]
}
</script>
I really got stuck for hours on this because just by the code, it should work I would say.
You are not using a proper parser like vueify to properly parse .vue files in your webpack/gulp script.

Categories