VueJS PSPDFKit Integration - vue.js

I am trying to integrate PSPDFKit (standalone) with a VueJS project, but struggling with some unexpected behaviour.
For background:
I have a component which includes the import PSPDFKit from 'pspdfkit' statement. When I run the Vue application and navigate to the view that utilises this component, I get an Uncaught SyntaxError: Unexpected token '<' - suggesting that I'm encountering HTML instead of the intended library.
However, when running locally, if I comment out the import line, wait for the PSPDFKit is not defined errors, then un-comment the import line, the viewer loads as expected.
Can anyone shed any light on what's happening here? I'm struggling to diagnose the issue and ensure the integration works as expected on first load. Component code is below.
Thanks
<template>
<div class="container"></div>
</template>
<script>
import PSPDFKit from 'pspdfkit'
export default {
name: 'PSPDFKitWrapper',
props: ["documentUrl", "licenseKey", "baseUrl"],
_instance: null,
mounted: function ()
{
this.load();
},
methods: {
load()
{
PSPDFKit.load({
document: this.documentUrl,
container: ".container",
licenseKey: this.licenseKey,
baseUrl: this.baseUrl
})
.then(instance =>
{
this._instance = instance;
this.$emit("update:error", "");
})
.catch(err =>
{
PSPDFKit.unload(".container");
this.$emit("update:error", err.message);
});
},
unload()
{
if (this._instance) {
PSPDFKit.unload(this._instance);
this._instance = null;
} else {
PSPDFKit.unload(".container");
}
}
}
}
</script>
<style scoped>
.container {
width: 100%;
height: 90vh;
}
</style>

Related

Vue js 3 render dynamic component template from server

I have a problem with Vue 3, using vue from CDN.
I want to use a template generated by the server, the template is changed but methods and data are not bound.
<script>
// reproduction of the issue in vue3 vite
import { compile, computed, h } from 'vue/dist/vue.esm-bundler'; // vite
// import { compile, computed, h } from 'vue/dist/vue.esm-bundler'; // webpack
export default {
data() {
return {
htmlTemplate: '<span #click="test()">this is a test {{ testVariable }}</span>', // This is a test from what would be loaded from the server
testVariable: 'test variable',
}
},
methods: {
test() {
console.log('test');
}
},
render() {
const textCompRef = computed(() => ({ render: compile(this.htmlTemplate) }));
console.log('textCompRef', textCompRef);
return h(textCompRef.value);
}
}
</script>
When I click on this is a test then vue#3:1807 Uncaught TypeError: test is not a function
Can someone point me in the right direction?
Thanks in advance
I tried setting the template in the create life cycle with this.$options.template = response from the server that worked on 3-rd click and was not changing when new template is loaded.

Npm package Vue3 UI library don't run in Nuxt

I have a UI library with Vue3 Composition API components. It works fine in other Vue projects but there is a problem in Nuxt 2.6 project.
My library has component Badge
// components/badge.vue
<template>
<div :class="classes">{{ label }}</div>
</template>
<script>
import { computed, reactive } from 'vue';
export default {
name: 'badge',
props: {
label: {
type: String,
required: true,
},
highlighted: {
type: Boolean,
default: false,
}
},
setup(props, { emit }) {
props = reactive(props);
return {
classes: computed(() => ({
'badge': true,
'badge--highlighted': props.highlighted
})),
onClick() {
emit('click');
}
}
},
}
</script>
<style lang="scss">
#import '_variables';
.badge {
font-size: $badge-font-size;
border-radius: $badge-border-radius;
background-color: $badge-bg;
color: $badge-color;
display: inline-block;
line-height: $badge-line-height;
padding: $badge-padding;
&--highlighted {
background-color: $badge-bg-accent;
color: $badge-color-accent;
}
}
</style>
I export component for npm package
// components/index.js
export { default as Badge } from './Badge/Badge.vue';
Build npm package with Umd and Es files and publish as private package
In Nuxt project I installed package and created plugin to use it.
// plugins/CupPatternLibrary.ts
import Vue from 'vue';
import * as CupPatternLibrary from '#cambridgecore/cup-pattern-library';
Vue.use(CupPatternLibrary);
import {
Badge
} from '#cambridgecore/cup-pattern-library'
Vue.component('Badge', Badge)
Registered plugin
// nuxt.config.js
plugins: [
'~/plugins/CupPatternLibrary.ts'
],
and want to use it
// landing-page.vue
<Badge label="xxx" />
And now i get TypeError and browser display error
Cannot read properties of undefined (reading 'classes')
in file
node_modules\#cambridgecore\cup-pattern-library\dist\cup-pattern-library.umd.js:2:416
So my question is how to make npm package to work with Nuxt project

vue bootstrap toaster instantly vanishing / hiding itself

In my Vue Bootstrap (v2.21.2) Web-App i want to use Toasts to present some errors to the user. Those errors are produced by the REST-API-client. In my vb-components i catch those errors and call a function which itself uses https://bootstrap-vue.org/docs/components/toast#toasts-on-demand this.$bvToast.toast() to dynamically create and show the error-message.
As expected the toast is created but will instantly hide itself again. I tried disabling the auto-hide property and play around with the timeout which had no effect. Since i am calling this function in some sub-components i also tried calling this.$root.$bvToaster.toast() but the toasts are still only showing for some 100 microseconds or so.
The relevant (reduced) code-extracts of my project:
App.vue:
<template>
<div id="app">
<Navbar #viewChanged="view = $event;" />
<Pki v-if="view == 'pki'" />
</div>
</template>
<script>
import Navbar from "./components/Navbar.vue";
import Pki from './components/Certificates'
export default {
data() {
return {
view: null
}
},
name: "FooBar",
components: {
Navbar,
Pki
},
};
</script>
Certificates.vue:
<template>
<!-- ... -->
</template>
<script>
// ...
mounted() {
this.getCertificates();
},
methods: {
alert(title, content, variant = 'danger') {
this.$bvToast.toast(content, {
title: title,
toaster: 'b-toaster-bottom-right',
variant: variant,
solid: true,
appendToast: true,
autoHideDelay: 10000
});
},
getCertificates() {
axios.get("/v1/pki/certificates")
.then((response) => {
// ...
});
})
.catch((error) => {
this.alert('API Error', 'failed to fetch certificate list (' + error.message + ')');
console.log('getCertificates(): HTTP ERROR ' + error.response.status + ' (' + error.response.data + ')');
});
}
}
</script>
If you are using bootstrap 5 just add this css
.toast:not(.show) {
display: block;
}
I think you don't have the appropriate version of the bootstrap css.
e.g 4.5.3 bootstrap css
and after load the vue bootstrap
Had the same issue, and this solved it

How to import a Vue component that is packaged into a JavaScript library?

I am attempting to package a Vue component into a JavaScript library and then use it in another project using vue-sfc-rollup.
I am able to package the component just as the README says to do. Then when I copy the .min.js file into another project and attempt to use the component, I always get the error:
Vue warn handler: Failed to mount component: template or render function not defined.
The way I'm trying to use the component from inside another Vue component is this:
import Vue from 'vue'
import MyComponent from '../lib/my-components.min.js'
Vue.use(MyComponent)
Then in the components section:
components: {
'my-component': MyComponent
}
Then in the template:
<my-component></my-component>
What am I missing here? What is the correct way to use the component in another project?
EDIT: Adding component code in response to comment.
<template>
<div class="my-component">
<p>The counter was {{ changedBy }} to <b>{{ counter }}</b>.</p>
<button #click="increment">
Click +1
</button>
<button #click="decrement">
Click -1
</button>
<button #click="increment(5)">
Click +5
</button>
<button #click="decrement(5)">
Click -5
</button>
<button #click="reset">
Reset
</button>
</div>
</template>
<script>
export default {
name: 'MyComponent', // vue component name
data() {
return {
counter: 5,
initCounter: 5,
message: {
action: null,
amount: null,
},
};
},
computed: {
changedBy() {
const {
message
} = this;
if (!message.action) return 'initialized';
return `${message?.action} ${message.amount ?? ''}`.trim();
},
},
methods: {
increment(arg) {
const amount = (typeof arg !== 'number') ? 1 : arg;
this.counter += amount;
this.message.action = 'incremented by';
this.message.amount = amount;
},
decrement(arg) {
const amount = (typeof arg !== 'number') ? 1 : arg;
this.counter -= amount;
this.message.action = 'decremented by';
this.message.amount = amount;
},
reset() {
this.counter = this.initCounter;
this.message.action = 'reset';
this.message.amount = null;
},
},
};
</script>
<style scoped>
.my-component {
display: block;
width: 400px;
margin: 25px auto;
border: 1px solid #ccc;
background: #eaeaea;
text-align: center;
padding: 25px;
}
.my-component p {
margin: 0 0 1em;
}
</style>
I found one way to do this at this Stack Overflow question: Register local Vue.js component dynamically.
I got it to work by implementing a simpler version of the solution shown there. I removed the component section from the outer component, then added this created() lifecycle hook:
created() {
console.log('pages/PageHome.vue: created(): Fired!')
// From https://stackoverflow.com/questions/40622425/register-local-vue-js-component-dynamically
// "This is how I ended up importing and registering components dynamically to a component locally"
const componentConfig = require('../lib/components/my-component.js')
console.log('pages/PageHome.vue: created(): componentConfig.default = ')
console.log(componentConfig.default)
const componentName = 'my-component'
console.log('pages/PageHome.vue: componentName = ' + componentName)
this.$options.components[componentName] = componentConfig.default
}
The component is imported using a require() call, then registered locally by adding it to the this.$options.components dictionary. The secret sauce is to add .default to the componentConfig expression. This doesn't seem to be formally documented anywhere.
Editorial comment: I'm surprised the Vue documentation pays such little attention to distribution patterns for re-usability. As great as the Vue docs are, this is a glaring omission.

TypeScript compiler can't find injected property in Vue Object Component

I'm writing a couple of examples for work, and one that's hanging me up is injecting a service that's provided during Vue's bootstrapping.
This "works" (I can access it, it compiles, and runs), and there are no problems or complaints with my JavaScript version nor the Class-Component TypeScript version, but the compiler complains that this.sampleService doesn't exist in my component when using the Vue object API with TypeScript.
Am I missing something?
<template>
<div class="app">
{{message}} <fake-button :text="buttonText" #some-click-event="handleClickEvent"></fake-button>
</div>
</template>
<style lang="scss">
.app {
$background-color: #9f9;
$foreground-color: #000;
background: $background-color;
color: $foreground-color;
}
</style>
<script lang="ts">
import Vue from 'vue'
const App = Vue.extend({
components: {
FakeButton: () => import('#client/components/fake-button/fake-button-object-typescript.vue')
},
data: function () {
return {
message: 'Hello World - App Object TypeScript',
buttonText: 'Click Me'
}
},
inject: {
sampleService: 'sampleService'
},
methods: {
handleClickEvent(someVal?: string) {
console.log('App', 'handleClickEvent', someVal);
}
},
beforeCreate() {
console.log('App', 'beforeCreate');
},
created() {
console.log('App', 'created');
},
mounted() {
console.log('App', 'mounted');
// TODO: While this compiles, TypeScript complains that it doesn't exist
console.log('this.sampleService.getDate()', this.sampleService.getDate());
}
});
export default App;
</script>
ERROR in vue-test/src/client/containers/app/app-object-typescript.vue.ts
[tsl] ERROR in vue-test/src/client/containers/app/app-object-typescript.vue.ts(35,56)
TS2339: Property 'sampleService' does not exist on type 'CombinedVueInstance<Vue, { message: string; buttonText: string; }, { handleClickEvent(someVal?: s...'.
My solution for this problem was to create a interface for the injection. In you example that would be something like the follwing:
<script lang="ts">
import { SampleServiceInjection } from '...';
const App = ( Vue as VueConstructor<Vue & SampleServiceInjection> ).extend({
inject: {
sampleService: 'sampleService'
} as Record<keyof SampleServiceInjection, string>,
// etc.
});
</script>
You can use that very interface in component that provides the service as well:
export interface SampleServiceInjection {
sampleService: SampleService; // or whatever type your service has
}
export default Vue.extend({
provide(): SampleServiceInjection {
return {
sampleService: new SampleService(),
};
},
// etc.
});
Try adding provide. Check example below. I would suggest using vue-property-decorator since your are leveraging typescript.
inject: {
sampleService: 'sampleService'
},
provide () {
return {
sampleService: this.sampleService,
}
}