gojs with vuejs and webpack - vue.js

I'm trying to set up a simple gojs diagram into vuejs + webpack.
I installed gojs with npm and imported it on my project in the main.js file: import go from 'gojs'
Now my problem is how to make things work in the component implementation, that's the code of the component Diagram.vue:
<template>
<div id="myDiagramDiv" style="width:300px; height:300px;"></div>
</template>
<script>
export default {
name: 'Diagram',
data: function () {
return {
nodeDataArray: [
{key:1, text:"Alpha"},
{key:2, text:"Beta"}
],
linkDataArray: [
{from:1, to:2}
]
}
},
methods: {
getUnits: function(){
var $ = go.GraphObject.make;
myDiagram = $(go.Diagram, "myDiagramDiv");
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
},
mounted: function(){
this.getUnits();
}
}
</script>
it compiles without error but I can only see a white empty box...

You haven't initialized the Diagram yet. That is typically done by calling go.GraphObject.make(go.Diagram, theHTMLDivElement, { . . . options . . .})
There is a complete but simple example of using GoJS in a Vue.js framework at https://gojs.net/latest/samples/vue.html.

ow my problem is how to make things work in the component implementation
There is an official (made by the GoJS team) VueJS + Webpack + GoJS project in the gojs-projects github, that you can use as an example: https://github.com/NorthwoodsSoftware/GoJS-projects

Related

Unable to load stencil components lib with Vue3 using Vite

I created a sample project to reproduce this issue: https://github.com/splanard/vue3-vite-web-components
I initialized a vue3 project using npm init vue#latest, as recommanded in the official documentation.
Then I installed Scale, a stencil-built web components library. (I have the exact same issue with the internal design system of my company, so I searched for public stencil-built libraries to reproduce the issue.)
I configured the following in main.ts:
import '#telekom/scale-components-neutral/dist/scale-components/scale-components.css';
import { applyPolyfills, defineCustomElements } from '#telekom/scale-components-neutral/loader';
const app = createApp(App);
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('scale-')
applyPolyfills().then(() => {
defineCustomElements(window);
});
And the same isCustomElement function in vite.config.js:
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('scale-')
}
}
})]
// ...
})
I inserted a simple button in my view (TestView.vue), then run npm run dev.
When opening my test page (/test) containing the web component, I have an error in my web browser's console:
failed to load module "http://localhost:3000/node_modules/.vite/deps/scale-button_14.entry.js?import" because of disallowed MIME type " "
As it's the case with both Scale and my company's design system, I'm pretty sure it's reproducible with any stencil-based components library.
Edit
It appears that node_modules/.vite is the directory where Vite's dependency pre-bundling feature caches things. And the script scale-button_14.entry.js the browser fails to load doesn't exist at all in node_modules/.vite/deps. So the issue might be linked to this "dependency pre-bundling" feature: somehow, could it not detect the components from the library loader?
Edit 2
I just found out there is an issue in Stencil repository mentioning that dynamic imports do not work with modern built tools like Vite. This issue has been closed 7 days ago (lucky me!), and version 2.16.0 of Stencil is supposed to fix this. We shall see.
For the time being, dropping the lazy loading and loading all the components at once through a plain old script tag in the HTML template seems to be an acceptable workaround.
<link rel="stylesheet" href="node_modules/#telekom/scale-components/dist/scale-components/scale-components.css">
<script type="module" src="node_modules/#telekom/scale-components/dist/scale-components/scale-components.esm.js"></script>
However, I can't get vite pre-bundling feature to ignore these imports. I configured optimizeDeps.exclude in vite.config.js but I still get massive warnings from vite when I run npm run dev:
export default defineConfig({
optimizeDeps: {
exclude: [
// I tried pretty much everything here: no way to force vite pre-bundling to ignore it...
'scale-components-neutral'
'#telekom/scale-components-neutral'
'#telekom/scale-components-neutral/**/*'
'#telekom/scale-components-neutral/**/*.js'
'node_modules/#telekom/scale-components-neutral/**/*.js'
],
},
// ...
});
This issue has been fixed by Stencil in version 2.16.
Upgrading Stencil to 2.16.1 in the components library dependency and rebuilding it with the experimentalImportInjection flag solved the problem.
Then, I can import it following the official documentation:
main.ts
import '#telekom/scale-components-neutral/dist/scale-components/scale-components.css';
import { applyPolyfills, defineCustomElements } from '#telekom/scale-components-neutral/loader';
const app = createApp(App);
applyPolyfills().then(() => {
defineCustomElements(window);
});
And configure the custom elements in vite config:
vite.config.js
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('scale-')
}
}
})]
// ...
})
I did not configure main.ts
stencil.js version is 2.12.1,tsconfig.json add new config option in stencil:
{
"compilerOptions": {
...
"skipLibCheck": true,
...
}
}
add new config option in webpack.config.js :
vue 3 document
...
module: {
rules:[
...
{
test: /\.vue$/,
use: {
loader: "vue-loader",
options: {
compilerOptions: {
isCustomElement: tag => tag.includes("-")
}
}
}
}
...
]
}
...

Load vue component (truly) dynamically from local file

Is it possible to load a vue component dynamically at runtime (in an electron app to build a plugin system)?
The component is in a specific file
Its path is only known at runtime
The component can either be precompiled (if that is possible, don't know) or is compiled at runtime
A simple example component is listed below
I tried the following approaches, both failing:
Require component
<template>
<component :is="currentComp"></component>
</template>
<script>
...
methods: {
loadComponent(path) {
const dynComp = eval('require(path)'); // use eval to prevent webpackresolving the require
this.currentComp = dynComp;
}
},
...
</script>
The import works, but the line this.currentComp = dynComp; Fails with error message:
Error in data(): "Error: An object could not be cloned."
Using the code presented here, but replace url with a local path
Fails with error message:
Failed to resolve async component: function MyComponent() {
return externalComponent('/path/to/Component.vue');
}
Reason: TypeError: Chaining cycle detected for promise #<Promise>
The used example component is the following:
// Example component
module.exports = {
template: `
<div>
<input v-model="value"/>
<button #click="clear">Clear</button>
<div>{{ value }}</div>
</div>`,
name: 'Example',
data() {
return {
value: '',
};
},
watch: {
value(value) {
console.log('change!');
},
},
methods: {
clear() {
this.value = '';
},
},
};
I found a solution:
Create the vue component as a SFC in a separate file (here src/Component.vue). I didn't try, but probably it works for inline components, too.
Precompile the component using vue-cli-service, which is already a dev dependency, if the project is created using vue-cli (It's nice to use vue-cli here, since the required loaders are already included):
yarn vue-cli-service build --target lib src/Command.vue
The component is compiled to different bundle types in the dist directory. The file [filename].umd.min.js can be imported now.
Import the component dynamically at runtime:
let MyComponent = eval(`require('/absolute/path/to/[filename].umd.min.js')`);
Vue.component('MyComponent', MyComponent);
The require is wrapped inside an eval to prevent webpack of trying to include the import in its bundle and transforming the require into a webpack__require.
(Optional) If the SFC component contains a <style>...</style> tag, the resulting css is compiled to a separate file. The css can be inlined in the js file by adding the following lines to the vue.config.js in the components project root:
module.exports = {
...
css: {
extract: false,
},
};
You can probably look into async loading:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html#Async-Components
and see this for a webpack lazy load example:
https://vuedose.tips/dynamic-imports-in-vue-js-for-better-performance/#the-meat%3A
These are just some things I would research for your requirements.

Cant import JS library to my Nuxt project

I have weird problem.
I want use this hover-effect library (https://github.com/robin-dela/hover-effect) in my nuxt project.
This i have in my contact.vue in script tags
import hoverEffect from 'hover-effect'
export default {
mounted() {
const effect = new hoverEffect({
parent: document.querySelector('.right-section'),
intensity: 0.3,
image1: require('#/assets/images/1.jpg'),
image2: require('#/assets/images/2.jpg'),
displacementImage: require('#/assets/images/dist2.jpg'),
})
},
}
And that effect works perfectly.. BUT when i refresh the page i got this error:
SyntaxError Cannot use import statement outside a module
So i tried add this plugin into plugins/hover-effect.js
import Vue from 'vue'
import hoverEffect from 'hover-effect'
Vue.use(hoverEffect)
then in nuxt.config.js
plugins: [{ src: '~/plugins/hover-effect', mode: 'client' }],
But nothing works.. its always error: hoverEffect is not defined. I tried another 20 ways with no success. I tried this effect in normal Vue project and it works but not in nuxt.js. Can somebody help me with this?
You can configure it in the head of the page:
Page.vue
export default {
head() {
return {
script: [
{src: '../dist/hover-effect.umd.js'}
]
}
},
...
mounted() {
const effect = new hoverEffect({
parent: document.querySelector('.right-section'),
intensity: 0.3,
image1: require('#/assets/images/1.jpg'),
image2: require('#/assets/images/2.jpg'),
displacementImage: require('#/assets/images/dist2.jpg'),
})
},
modules: [
// Doc: https://axios.nuxtjs.org/usage
'#nuxtjs/axios',
// Doc: https://github.com/nuxt/content
'#nuxt/content',
'hover-effect'
],
Have you tried to add hover-effect library to modules in nuxt.config.js file? All I did was install the package and add it to the module and then have the same code as your script tag. Hope it helped you!

plugin is not defined in instance.vue

I struggle to add a plugin in Nuxt.js. I have been looking to the doc and all kind of similar problems, but I got the same error: simpleParallax is not defined.
I tried different approach on all files
nuxt.config.js:
plugins: [
{src: '~/plugins/simple-parallax.js', mode:'client', ssr: false}
],
plugins/simple-parallax.js:
import Vue from 'vue';
import simpleParallax from 'simple-parallax-js';
Vue.use(new simpleParallax);
index.vue:
Export default {
plugins: ['#/plugins/simple-parallax.js'],
mounted() {
var image = document.getElementsByClassName('hero');
new simpleParallax(image, {
scale: 1.8
});
}
}
Error message:
ReferenceError: simpleParallax is not defined.
The best solution I found out so far is to register simpleParallax on the Vue prototype like so in a plugin nuxt file with the name simple-parallax.client.js:
import Vue from 'vue';
import simpleParallax from 'simple-parallax-js';
Vue.prototype.$simpleParallax = simpleParallax;
Also my nuxt.config.js file if anyone would like to verify that as well:
plugins: [
{src: '~/plugins/simple-parallax.client.js', mode: 'client', ssr: false}
],
I then have access to the plugin before instantiation in my case in the mounted life cycle of the primary or root component to grab the desired HTML elements and instantiate their individual parallax with the newly added global method this.$simpleParallax
For example I can then intiate a certain HTML element to have its parallax like so:
const someHTMLElement = document.querySelectorAll('.my-html-element');
const options = {...} // your desired parallax options
new this.$simpleParallax(someHTMLElement, options);
Actually you don't need to use plugin here.
Just import simpleParallax from 'simple-parallax-js' in your component and init it with your image in mounted hook.
index.vue:
import simpleParallax from 'simple-parallax-js'
export default {
...
mounted() {
// make sure this runs on client-side only
if (process.client) {
var image = document.getElementsByClassName('thumbnail')
new simpleParallax(image)
}
},
...
}
And don't forget to remove previously created plugin, it's redundant here.

Error- Failed to mount component: template or render function not defined. (found in root instance)

I am using browserify and NPM to pull in vue.
var Vue = require('vue');
module.exports = function() {
new Vue({
el: '#app',
data: {
message: 'Hello Vue2!'
}
})
}
I get the mount error. This could be the issue. https://v2.vuejs.org/v2/guide/installation.html#Standalone-vs-Runtime-only-Build
However when I add the line to my package.json
"browser": {
"vue": "vue/dist/vue.common"
},
I get an error
Error: Parsing file /Users/mark/testsite/node_modules/vue/dist/vue.common.js: Line 6278: Invalid regular expression
My html is simply
<div id="app"></div>
I think you need to use aliasify for this (I assume you already have vueify installed):
npm install alisaify --save dev
then in your package.json you can do:
"browserify": {
"transform": [
"aliasify",
"vueify"
]
},
"aliasify": {
"aliases": {
"vue": "vue/dist/vue.common"
}
}
To install vueify you can simply do:
npm install vueify --save-dev
You can then use single file components
The runtime build and standalone build are the source of a lot of confusion, so I just want to explain how these builds work and what this mount error really is.
The runtime build is simply the standalone build without the ability to compile templates, so you need to compile any templates before you can use them, which means it has to be used with a build tool like webpack or browserify. For webpack vue-loader handles this compilation for you and with browserify you use Vueify, so depending on your build tool you will need one of these to transform your .vue files into render functions.
Internally this compilation step will take something that looks like this:
<template>
<div>
Hello World
</div>
</template>
And convert it into something that looks like this:
render: function(createElement) {
return createElement('div','Hello World');
}
Now, for this to work you need one entry point for your entire App, and this entry point needs to be mounted on your main vue instance:
import Vue from 'vue' // pull in the runtime only build
import App from './app.vue' // load the App component
new Vue({
el: "#app",
// This render function mounts the `App` component onto the app div
render: (h) => {
return h(App)
}
});
Now on your compile step Vue will compile all your components for you, with the App component being the entry point. So in App.vue you may have something that looks like this:
<template>
<message :msg="Hello"></message>
</template>
<script>
// import the message component so we can display a message
import message from './message.vue';
export default {
components: {
message
}
}
</script>
OK, so why are you getting the render function not defined error? Simply, because you haven't defined a render function. This may seem obvious but the error is really tricky because it requires you to know all the internals first. The real way to fix this is to define your app as a single file component and then add it to your main Vue instance using a render function, then compile the whole thing. So your entire app will now look like:
message.vue:
<template>
<div>{{message}}</div>
</template>
<script>
export default {
data(){
return {
messsage: "Hello Vue2!"
}
}
}
</script>
main.js
import Vue from 'vue';
import message from './message.vue';
new Vue({
el: '#app',
render: (createElement) => {
return createElement(message)
}
});
Now Vue will hunt down your element with the id "app" and inject your message component into it.
So, let's just see how you might write this if you had to do it manually:
Vue.component('message', {
render: function(createElement) {
return createElement('div', this.msg)
},
data() {
return {
msg: "Hello Vue2!",
}
}
});
new Vue({
el: "#app",
render: function(createElement) {
return createElement('message')
}
})
Here's the JSFiddle (which is really using the runtime only build)
: https://jsfiddle.net/6q0Laykt/
The standalone build has the compiler included, so you don't need to do this compilation step. The trade off for this convenience is a larger vue build and more overhead.
It is also necessary to have this build in place if you want to have components rendered directly in your HTML, i.e. when not using single file components.
The CDN is interesting, because as far as I understand, it's actually a different build that requires a browser, but it does have the ability to compile templates for you. So, that would be why your code runs with the CDN.
Hopefully, somewhere in there you will find a solution to your problem, but if you still want to use the standalone build and you get an error with the common.js file it may be worth seeing if the non common version works, which was previously recommended as the file to alias:
"aliasify": {
"aliases": {
"vue": "vue/dist/vue"
}
}