How to make Intellisense/autocompletion work with VSCode/Volar when using the provide/inject functionality with custom plugins in Vue 3? - vue.js

Disclaimer: This project does NOT have Typescript, and migrating to it would be a huge task. Although if there are no better solutions, then I wouldn't mind if somebody answered how to solve the problem with Typescript without having to adapt the whole project to it.
The project is built using Vite
I have a plugin #/plugins/ckie.js:
export default {
install: (app, options) => {
const ckie = {
test: () => { // The test method
console.log('The ckie plugin is working!')
},
}
app.provide('ckie', ckie)
},
}
My main.js:
import { createApp } from 'vue'
import App from './App.vue'
import ckie from './plugins/ckie'
createApp(App)
.use(ckie)
.mount('#app')
And when I use it in a component:
<script setup>
import { inject } from 'vue'
const ckie = inject('ckie') // Here VSCode didn't know what plugins (strings) can be injected
const test = ckie.test // Here Intellisense didn't show any hints for the test method
</script>
Yes, I tried restarting the Vue server, reloading the VSCode window.

Related

FontAwesome not working with Vue3/TS over undefined props

Vue 3 + TypeScript. Trying to get fontawesome to work.
The following code results in errors in devtools (while the cli is
clean) - seems like props are undefined, so I tried various types of binding as well: :icon="['fa', 'redo']" - to no avail.
True that I am also getting a TS2307 over the vue-fontawesome
import, even though the package is installed and the index.d.ts with
correct member export export const FontAwesomeIcon is there. (v. 2.0.2)
Any clue on what is going wrong?
main.ts
// font-awesome
import { library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome'; // <-- TS2307 squiggles
import { faRedo } from '#fortawesome/free-solid-svg-icons';
library.add(faRedo);
// bootstrap
createApp(App)
.use(store)
.use(router)
.component('font-awesome-icon', FontAwesomeIcon)
.mount('#app');
reload.vue (custom component)
<template>
<span #click="doReload">
Reload
<font-awesome-icon icon="redo" />
</span>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Reload',
setup(props, context) {
const doReload = () => {
context.emit('reload');
};
return {
doReload,
};
},
});
</script>
I'm actually not sure it's the only reason here, but: 2.0.2 version of vue-fontawesome actually has Vue 2 is its peer dependency, and you mentioned Vue 3 is used in your project. Quoting their doc:
Using Vue 2.x
$ npm i --save #fortawesome/vue-fontawesome#latest
Using Vue 3.x
$ npm i --save #fortawesome/vue-fontawesome#prerelease
The latter installs version 3.0.0-3, not 2.0.2.

Composition api use this

I am using Vue 3 with Composition API, and I want to use a third-party package (for example #meforma/vue-toaster), and it should be used like this (in Options API):
import Toaster from '#meforma/vue-toaster';
createApp(App).use(Toaster).mount('#app')
and then in the component:
this.$toast.show(`Hey! I'm here`);
this.$toast.success(`Hey! I'm here`);
this.$toast.error(`Hey! I'm here`);
this.$toast.warning(`Hey! I'm here`);
this.$toast.info(`Hey! I'm here`);
But this is not working inside the Composition API's setup() function.
#meforma/vue-toaster installs $toast on the application context, which can be accessed from getCurrentInstance().appContext.globalProperties in setup():
<template>
<button #click="showToast">Show toast</button>
</template>
<script>
import { getCurrentInstance } from 'vue'
export default {
setup() {
const $toast = getCurrentInstance().appContext.globalProperties.$toast
return {
showToast() {
$toast.show(`Hey! I'm here`)
$toast.success(`Hey! I'm here`)
$toast.error(`Hey! I'm here`)
$toast.warning(`Hey! I'm here`)
$toast.info(`Hey! I'm here`)
setTimeout($toast.clear, 3000)
}
}
}
}
</script>
i've the same issue.
So i've found and easy way to do:
I'm using Vite BTW.
my main.js
import { createApp } from 'vue'
import App from './App.vue'
import Toaster from '#meforma/vue-toaster';
let app = createApp(App)
app.use(Toaster, {
position: 'top-right'
}).provide('toast', app.config.globalProperties.$toast)
app.mount('#app')
my component:
import { inject } from 'vue'
export default {
name: 'table-line',
setup(props) {
const toast = inject('toast');
toast.success(`it works !`)
return {toast}
}
}
Hope it could be helpful
The setup function runs one-time before the component is created. It lacks the this context and may not be where you would want to place these anyway. You could try putting it into a method that you can call via a button.

How to inject Vuetify into my custom vue plugin

I would like to create a Vue plugin with a function which programatically renders a Vue component. That component depends on Vuetify. Everything works fine if I use vanilla HTML/CSS in that component, but using Vuetify-related things in there (e.g. a ) does not work. I assume that I didn't inject vuetify itself into the component correctly.
In my custom component, I tried importing every Vuetify component separately, but without success. I also tried creating the component with the syntax: new Vue({vuetify}), but also without success.
import MyCustomComponent from '#/components/MyCustomComponent'
import vuetify from '#/plugins/vuetify';
export default {
install(Vue, options) {
function renderMyCustomComponent() {
const CustomComponent= Vue.extend(MyCustomComponent)
Vue.use(vuetify)
let instance = new CustomComponent()
instance.$mount()
document.body.appendChild(instance.$el)
}
Vue.prototype.$renderMyComponent = renderMyCustomComponent
}
}
The error message indicates, that vuetify (or at least some of it's properties) are not available in my component
[Vue warn]: Error in getter for watcher "isDark": "TypeError: Cannot read property 'dark' of undefined"
HINT/EDIT: I am using Vuetify 2.0. The way Vuetify is injected into the app changed a little bit. Here's the code of my vuetify plugin file:
import Vue from 'vue';
import Vuetify from 'vuetify';
import 'vuetify/dist/vuetify.min.css';
import de from 'vuetify/es5/locale/de';
Vue.use(Vuetify)
export default new Vuetify({
theme: {
themes: {
light: {
primary: '#3f51b5',
secondary: '#b0bec5',
accent: '#8c9eff',
error: '#b71c1c'
},
},
},
});
Not sure if you solved this issue, but I had the same problem where Vuetify in a plugin would not be initialized correctly.
Vuetify documentation states that you need to define a vuetify option when creating your vue instance:
new Vue({
vuetify,
}).$mount('#app')
Fortunately, custom Vue plugins has an options parameter that we can use.
Here is the code that consumes your plugin:
const options = {}; // add other options here! (vuex, router etc.)
Vue.use(YourCustomPlugin, options);
new Vue(options).$mount('#app');
And here is your plugin code:
import vuetify from "./src/plugins/vuetify";
export default {
install(Vue, options) { // options is undefined unless you pass the options param!
Vue.component('my-custom-component', MyCustomComponent);
Vue.use(Vuetify);
options.vuetify = vuetify;
}
};
The vuetify module is very simple:
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
const opts = {}
export default new Vuetify(opts);
The problem is that you actualy don't export plugin itself in '#/plugins/vuetify';
import MyCustomComponent from '#/components/MyCustomComponent'
import Vuetify from 'vuetify';
export default {
install(Vue, options) {
function renderMyCustomComponent() {
Vue.use(Vuetify)
const CustomComponent= Vue.extend(MyCustomComponent)
let instance = new CustomComponent()
instance.$mount()
document.body.appendChild(instance.$el)
}
Vue.prototype.$renderMyComponent = renderMyCustomComponent
}
}

Problems with components after npm run build

Using Vue-cli plugin I've created small SPA application with Vuetify component framework. When I was running in dev mode everything was fine but in production mode there was problem with components 'installComponents' has already been declared
Then I've found out that this was connected with Tree shaking option that only worksi with webpack 4 in production mode. Or to be more precise including components that you need, instead of getting all of them.
So, instead of registering every single component I tried to use vuetify-loader to automatize "a la carte components" but I it seems that I'm missing something.
my plugins/vuetify.js
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import 'vuetify/src/stylus/app.styl'
Vue.use(Vuetify)
And my vue.config.js is like this
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')
module.exports = {
configureWebpack: {
plugins: [
new VuetifyLoaderPlugin()
]
}
}
And main.js is like this:
import Vue from 'vue'
import './plugins/vuetify'
import App from './App.vue'
import router from './router'
import store from './store/store'
import axios from 'axios';
Vue.config.productionTip = false
//adding main path to baseurl
axios.defaults.baseURL = "";
// Global settings for Axios
Vue.prototype.$http = axios;
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
And I'm still getting this error
Module parse failed: Identifier 'installComponents' has already been declared (35:7)
It's worth mentioning that my babel.config.js looks like this:
module.exports = {
presets: [
'#vue/app'
]
}
I'm using vue 2.5.21 and vuetify 1.3.0
UPDATE
I changed my vue.config.js with this lines
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/hr-map/'
: '/vue-map/',
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
// modify the options...
return options
})
}
}
But now in production I'm getting rather strange error for my components
Uncaught TypeError: T(...)(...) is not a function
at Module.56d7 (VectorFeatures.vue:37)
at r (bootstrap:78)
at Object.0 (bootstrap:151)
at r (bootstrap:78)
at i (bootstrap:45)
at bootstrap:151
at bootstrap:151
I must say that when I'm in dev environment everything works fine.

How to install flickity carousel with vuejs and nuxtjs

I'm a new vuejs developer. I have study vueje for a while and now I decided to develop a project using vuejs.
So I learn about nuxtjs which is server side rendering. everything goes well. I can use bootstrap4 with my project.
Now I would like to use flickity carousel https://flickity.metafizzy.co on my project and I found that there is a vuejs package on https://github.com/drewjbartlett/vue-flickity
I follow the instruction how to install this component to my project by
npm install vue-flickity --save
and put on some code
<script>
import Logo from '~/components/Logo.vue'
import Searchbar from '~/components/Searchbar.vue'
import axios from 'axios'
import Flickity from 'vue-flickity';
export default {
data () {
return {
has_location: false,
flickityOptions: {
initialIndex: 3,
prevNextButtons: false,
pageDots: false,
wrapAround: true
}
}
},
components: {
Logo,
Searchbar,
Flickity
}
}
</script>
but it show window is not defined
I have try this with another component like google map, it's show the same error.
Please tell me what wrong did I do and how to install new component to the project.
Thank you.
Nuxt.js use SSR to render your website server side, therefore window object is not accessible on node.js environment.
What you need to do is use the built-in no-ssr component to prevent Nuxt.js to render it on the server side.
You can simply do this:
<no-ssr>
<Flickity :options="...">
<!-- slides -->
</Flickity>
</no-ssr>
UPDATE: If you still get an error at this point, then load Flickity in
a custom Plugin that you will load with ssr disabled
Create a file named plugins/VueFlickity.js
import Vue from 'vue'
import Flickity from 'vue-flickity'
Vue.component('Flickity', Flickity)
Then in your nuxt.config.js your add:
module.exports = {
// ...
plugins: [
{ src: '~/plugins/VueFlickity.js', ssr: false }
]
}
Don't forget to remove the Flickity local component registration:
components: {
Logo,
Searchbar
// Flickity <-- remove this line
}
This was tested and is now fully working.
I fixed it with:
let Flickity = {};
if (process.browser) {
Flickity = require('flickity.js');
}
#rayfranco pointed a great way.:) The thing is that by doing this in that way You're importing this plugin globally, but not as local component which is better for performance.
So You can do it also like this:
let Flickity;
if (process.client) {
Flickity = require('vue-flickity')
}
export default {
components: {
Flickity
}
}
and use this component this way:
Important: <no-ssr>......</no-ssr> is deprecated in Nuxt > 2.9, so use
<client-only>
<Flickity :options="...">
<div class="carousel-cell">1</div>
<div class="carousel-cell">2</div>
<div class="carousel-cell">3</div>
</Flickity>
</client-only>
you can also look into brief example by Josh Deltener
https://deltener.com/blog/common-problems-with-the-nuxt-client-only-component/