Import npm package into a Vue.js Single File component - vue.js

I would like to use Jodit in a SFC, but I am not sure how this is supposed to be done. I realized there is a wrapper (jodit-vue), but for educational purposes, I would like to know how it's done without it. I created a Vue CLI project with default presets, and all I changed is the App.vue:
<template>
<div id="app">
<textarea id="editor" name="editor"></textarea>
</div>
</template>
<script>
import "../node_modules/jodit/build/jodit.min.js"
export default {
name: 'App',
created(){
let editor = new Jodit('#editor');
editor.value = '<p>start</p>';
}
}
</script>
<style>
#import "../node_modules/jodit/build/jodit.min.css" ;
</style>
This produces the error: error 'Jodit' is not defined no-undef, and
if I change the import to:
import Jodit from "../node_modules/jodit/build/jodit.min.js"
Then the compilation is fine, but the browser console says:
vue.runtime.esm.js?2b0e:1888 TypeError: _node_modules_jodit_build_jodit_min_js__WEBPACK_IMPORTED_MODULE_0___default.a is not a constructor
Admittedly, I am new to all of this, but pointing me to the right direction is appreciated.

The jodit module exports the Jodit constructor, so your component would import it like this:
import { Jodit } from 'jodit'
You'd also need the Jodit styles, which could be imported like this:
import 'jodit/build/jodit.min.css'
To create a Jodit instance, we need to provide an element or selector to an existing <textarea>. The Vue component's elements are available in the mounted() lifecycle hook (not in the created() hook), so that's where we would initialize:
export default {
mounted() {
const editor = new Jodit('#editor')
editor.value = '<p>start</p>'
},
}
demo

Related

Nuxt local import client only

I'm trying to use VuePlyr in Nuxt 2. Right now I have it working as a plugin /plugins/vue-plyr.js,
import Vue from 'vue'
import VuePlyr from '#skjnldsv/vue-plyr'
import 'vue-plyr/dist/vue-plyr.css'
Vue.use(VuePlyr)
but it is just used in one page, so I would like to remove it from the main bundle and just import it locally when used. I've tried this in my page (the template part was working when using the plugin).
<template>
<client-only>
<vue-plyr>
<div data-plyr-provider="vimeo" :data-plyr-embed-id="id" />
</vue-plyr>
</client-only>
</template>
<script>
import 'vue-plyr/dist/vue-plyr.css'
import Vue from 'vue'
export default {
async mounted () {
const VuePlyr = await import('#skjnldsv/vue-plyr')
Vue.use(VuePlyr)
}
}
</script>
but unfortunately, I'm getting this error
[Vue warn]: Unknown custom element: <vue-plyr> - did you register the component correctly?
Any idea how I could achieve this? Related with How to make a dynamic import in Nuxt?
You could import it like that
export default {
components: {
[process.client && 'VuePlyr']: () => import('#skjnldsv/vue-plyr'),
}
}
As mentioned in a previous answer.
In your nuxt config define the plugin as client only:
plugins: [
{ src: "~/plugins/vue-plyr.js", mode: "client" }
],
Then also make sure there's a client-only tag around the use of the component:
<template>
<client-only>
<vue-plyr>
<div data-plyr-provider="vimeo" :data-plyr-embed-id="id" />
</vue-plyr>
</client-only>
</template>
Edit: importing the component again in the mounted method isn't necessary if you added it as a plugin

Call app.use for plugin in the component itself in vue 3

I'm building a component library that uses the v-tooltip plugin. So I need to install and use the plugin in the component itself instead using it globally with app.use().
I've read so many posts, and what I've tried so far doesn't work for my case.
I know that I can access the app in the Composition API as:
import VTooltip from 'v-tooltip';
import 'v-tooltip/dist/v-tooltip.css';
const App = getCurrentInstance().appContext.app;
App.use(VTooltip);
but that doesn't work, and I get this warning:
[Vue warn]: Component is missing template or render function.
Any help would be greatly appreciated.
to use this plugin in the component itself, you can try to do something like this:
<template>
<button v-tooltip="/* your code */"> Custom button </button>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import VTooltip from "v-tooltip";
export default defineComponent({
directives: {
tooltip: VTooltip.VTooltip,
"close-popover": VTooltip.VClosePopover,
"v-popover": VTooltip.VPopover,
},
});
</script>
Thanks #Rago, you gave me an idea with the directives. The solution was really simple in this case... At the moment v-tooltip is undergoing a package rename (to floating-vue), so with the new plugin you can decide if you want to use a component or a directive.
This is the solution:
<template>
...
<span v-tooltip="help" class="form-help">?</span>
...
</template>
<script>
import 'floating-vue/dist/style.css';
import { VTooltip } from 'floating-vue';
export default defineComponent({
directives: {
tooltip: VTooltip,
},
...
});
</script>
And for the Composition API you just import it, and Vue will automatically detect the directive if you follow the naming convention - putting v in front of the directive:
import 'floating-vue/dist/style.css';
import { VTooltip } from 'floating-vue';
const vTooltip = VTooltip;

TypeError: _vm.moment is not a function in Vuejs

I have a problem migrating to moment on Vuejs.
After running npm install vue-moment and adding to my script:
<script>
const moment = require('vue-moment');
...
</script>
I added this into my <template> :
<h1>{{moment('2017-12-20 11:00').fromNow()}}</h1>
And I get this error:
[Vue warn]: Error in render: "TypeError: _vm.moment is not a function"
You can use it globally as #red-X said, but you can add it only on your component:
import moment from 'moment'
export default {
data: () => ({
moment: moment
})
}
And then you can access it in your HTML template.
But i recommand you to use computed vars for using this kind of code, and to not have logic in your html template, just render computed vars inside your templates for readability.
And with this solution, you don't need to have moment library available globally or in your component, just the import.
Here it's an example :
import moment from 'moment'
export default {
computed: {
distanceFromNow() {
return moment('2017-12-20 11:00').fromNow()
}
}
}
And in your template :
<template>
<div>
{{ distanceFromNow }}
</div>
</template>
import * as momentTemp from 'moment';
const moment = momentTemp["default"];
Did you add the 'moment' attribute to the lifecycle 'methods',forExample:
methods:{
moment,
function a(){},
}
Did you add moment to the global Vue object like this:
const moment = require('vue-moment');
Vue.use(moment)
Just adding it to the local scope of a component will not make it available for use in the template. Everything referenced in the template is taken from the component itself.

Nested single file components - vue.js with electron-forge

I am trying electron for the first time and I am blown away by it. I have hit a wall, though, when trying to use single file vue.js components using electron-forge. My problem is the following:
I create a project using the vue.js template and run it. Works and looks great. I have a single file page with an index file that looks like this:
<div id="test"></div>
</body>
<script>
import Vue from 'vue';
import Test from './test';
const app = new Vue(Test).$mount('#test');
app.text = "Electron Forge with Vue.js!";
</script>
So far, so good. It imports Test, which is a single file component and renders it.
Now, I would like to have other single file components nested in this main component. For example, I would like to have the following, in my app file called test.vue
<template>
<h2>Hello from {{text}}</h2>
</template>
<script>
import About from './About.vue'
export default {
components: {
appAbout: About,
},
data () {
return {
text: 'Electron'
}
}
}
</script>
Again, so far so good. I can run the app with no errors so the component is being imported.
Here comes my problem: if I now try to render the component using <appAbout></appAbout>, as I have done before in web apps with vue.js, I get the following error.
It basically says that I am not using a single root element in my component, which is really strange because my component looks like this:
<template lang="html">
<div>Hello from component</div>
</template>
<script>
export default {
}
</script>
<style lang="css">
</style>
I am stuck. Can someone please help?
So I have tried a few different things with no success, like using or even as the component names.
I also have tried these two ways of starting the vue:
The way you get with electron-forge
const app = new Vue(App).$mount('#app')
and the way I learned
new Vue({el: '#app', render: h => h(App)})
Nothing seems to work...
Define your component like this :
export default {
components: {
'app-about': About
}
}
Then use it in template like this (with kebab-case) :
<app-about></app-about>
About your compiling template error you need to wrap everything in test.vue in a root element :
<template>
<div>
<h2>Hello from {{text}}</h2>
<app-about></app-about>
</div>
</template>

Why is moment js not working in mounted vue component?

When I put moment on the method like this:
<template>
...
</template>
<script>
export default{
...
methods:{
...
add(event){
let current = moment()
}
}
}
</script>
and then call the add method, it works without error.
But if I put moment on the mounted like this:
mounted(){
let currentAt = moment()
}
it does not work. It returns the following error:
[Vue warn]: Error in mounted hook: "ReferenceError: moment is not
defined"
How can I solve this?
Since you are using .vue files, I am assuming you are using the vue-loader or some other loader within the webpack ecosystem. If you are, then you can do something like the following:
<script>
export default{
import moment from 'moment'
...
methods:{
...
add(event){
let current = moment()
}
}
}
</script>
Then, just make sure you either execute yarn add moment or npm i -s moment.
If you make a bundle, at the beginning of script, you need to import moment from 'moment'.
If you import the files in your HTML, put a script tag in the HTML:
<script src="moment.js"></script>.