How to use Vue2 plugins on Vue3 Composition API? - javascript

I am migrating my Vue2 App to use #vue/composition-api (Vue version still 2 not 3).
In Vue2 I use my plugins like this.$session however this won't work on Vue3. The solution I found was to use like this:
setup (props, context) {
context.root.$session // works fine
context.root.$anotherPlugin
}
However in Vue3 context.root is #deprecated so when I migrate to Vue3 it will not work anymore.
Is there any other way to access Vue instance inside setup()? I think if I could access it I can use those plugins normally on Vue3.

Use provide & inject
Provide
const app = createApp(App);
app.provide('someVarName', someVar); // Providing to all components here
Inject:
const { inject } = Vue;
...
setup() {
const someVar = inject('someVarName'); // injecting in a component that wants it
}

Related

How can I access/mutate Vue component properties from vanilla JS

I have a Vue 2 project made with Vue CLI, and I plan to distribute it as a library, ideally with the dependencies and Vue syntax stuff abstracted away by some kind of wrapper script. I would like to allow this kind of interaction:
// mount the component on a plain JS webpage
const myComponent = new MyComponent('#my-component');
// handle events from the component in vanilla JS
myComponent.on('load', someHandler);
// (A.) call a component method and get a return value
const processedData = myComponent.process(123);
// (B.) access/mutate reactive component data properties
myComponent.setMessage('Hello world!');
I have tried changing the "build target" to build a Libary or a Web Component as mentioned in the Vue documentation. I can mount the library component just fine, and handle events, but it doesn't mention how I might interact with the component data from outside the Vue VM (see comments A and B).
How can I access Vue component methods and data properties from outside the Vue VM, in vanilla JS?
To access the Vue component properties (and methods) outside of the VM, you can mount it with a "template ref" like this:
const vm = new Vue({
components: {
MyComponent,
},
template: `
<my-component
ref="myComponent"
/>
`,
}).$mount('#mount-element");
and then you can call its methods like this:
vm.$refs.myComponent.someFunction();
You'll get the returned values and it will access/mutate reactive properties inside the VM as expected.
To use the class syntax described in the original question, we can create a simple class to wrap the vue component:
// import the component built by Vue CLI with the "library" build target
// (puts `MyComponent` in the global namespace)
import './MyComponent.umd.min.js';
import Vue from 'https://unpkg.com/vue#2/dist/vue.esm.browser.min.js';
export default class {
constructor(mountElement) {
// mount vue VM with a template ref to access its properties
const thisClass = this;
this.vm = new Vue({
components: {
MyComponent,
},
template: `
<my-component
ref="myComponent"
/>
`,
}).$mount(mountElement);
this.component = this.vm.$refs.myComponent;
}
// define methods that could call this.component's functions
someFunction() {
// do stuff
return this.component.someFunction()
}
}
It seems to work pretty well. A possible improvement would be to build the component library with a different tool, since Vue CLI v3 (with Vue v2 projects) can't output ESM module files, so the best we can do is a UMD modle that gets defined globally.

Vue - make helper for root component and all child component

Please is there a way to create a helper function on a root component in vue and also make the function accessible in all child components?
You can create helper functions and use it as a plugin. In case of you are using nuxt.js, you can create helpers.js in plugins and register it in nuxt.config.js file.
import Vue from 'vue'
import helpers from './helpers'
const plugin = {
install () {
Vue.prototype.$helpers = helpers
}
}
Vue.use(plugin)
In helpers.js, you can define all helper functions.
export default {
cloneObj(val) {
return JSON.parse(JSON.stringify(val));
}
};
Then you can use it in any child components like this:
this.$helpers.cloneObj()
You need to store it in a separate file because it's frustrating to pass it as a prop from one component to another and that's the main idea of why state management like Vuex is a better solution because it provides a centralized state manage which you can access from any component

Vue Composition API from outside of Vue

So, I've started using the Vue Composition API, and it's brilliant. I'm using it in a project that has Vue components, but also Vanilla JS. I'm building a notification system in Vue, as we are slowly moving everything that way.
I have the following code currently for adding a notification
export const useNotification = () => {
const notifications = ref([]);
const visibleNotifications = computed(() => {
return notifications.value.filter(notification => notification.visible === true).reverse();
});
const add = (notification: Notification) => {
notifications.value.push(notification);
};
};
I can get this adding perfectly from within Vue, but I want to also add a notification from the vanilla JS parts of the system. I've tried using useNotification().add() but I get the following error [vue-composition-api] must call Vue.use(plugin) before using any function. Basically, it wants me to use it inside Vue.
Any ideas on how I get this working?
Due to the shortcomings of using the vue-composition-api with vue2, and following the SO question here, I needed to add the following to type of my exported TS file
import Vue from 'vue';
import VueCompositionApi from '#vue/composition-api';
Vue.use(VueCompositionApi);

Add global variable in Vue.js 3

Anybody know how to do add a global variable in Vue 3 ?
in Vue 2 we use this in the main.js file:
Vue.prototype.$myGlobalVariable = globalVariable
The most direct replacement is app.config.globalProperties. See:
https://vuejs.org/api/application.html#app-config-globalproperties
So:
Vue.prototype.$myGlobalVariable = globalVariable
becomes:
const app = createApp(RootComponent)
app.config.globalProperties.$myGlobalVariable = globalVariable
This is scoped to a particular application rather than being global as it was with Vue.prototype. This is by design, all 'global' configuration options are now scoped to an application.
The relevant RFC is here:
https://github.com/vuejs/rfcs/blob/master/active-rfcs/0009-global-api-change.md
Properties added to globalProperties will be available via the component instance for all components within the application. So if you're using the Options API you'll be able to access them using this.$myGlobalVariable, just like you could with Vue.prototype. They'll also be available in the template without the this., e.g. {{ $myGlobalVariable }}.
If you're using the Composition API then you'll still be able to use these properties within the template, but you won't have access to the component instance within setup, so these properties won't be accessible there.
While hacks involving getCurrentInstance() can be used to access globalProperties within setup, those hacks involve using undocumented APIs and are not the recommended approach.
Instead, application-level provide/inject (also discussed in that RFC) can be used as an alternative to Vue.prototype:
const app = createApp(RootComponent)
app.provide('myGlobalVariable', globalVariable)
In the descendant component this can then be accessed using inject. e.g. With <script setup>:
<script setup>
import { inject } from 'vue'
const myGlobalVariable = inject('myGlobalVariable')
</script>
Or with an explicit setup function:
import { inject } from 'vue'
export default {
setup() {
const myGlobalVariable = inject('myGlobalVariable')
// Expose it to the template, if required
return {
myGlobalVariable
}
}
}
Or with the Options API:
export default {
inject: ['myGlobalVariable']
}
Docs: https://vuejs.org/api/application.html#app-provide
The idea here is that the component can explicitly declare the property rather than inheriting it by magic. That avoids problems like name collisions, so there's no need to use a $ prefix. It can also help to make it clearer where exactly a property is coming from.
It is common for the inject function to be wrapped in a composable. For example, the useRoute composable exposed by Vue Router is just a wrapper around inject.
In addition to globalProperties and provide/inject, there are various other techniques that might be used to solve the same problems as Vue.prototype. For example, ES modules, stores, or even global mixins. These aren't necessarily direct answers to the specific question posted here, but I've gone into more detail describing the various approaches at:
https://skirtles-code.github.io/vue-examples/patterns/global-properties.html
Which approach you prefer will depend on your circumstances.
How to add a global variable using Vue 3 and vue-cli (or Vite)
Note: You can drop the dollar sign from your $globalVariable and just use globalVariable, just like in the documentation.
Initially your main.js file looks something like this (adding router for common use case):
import { createApp } from 'vue'
import { App } from './App.vue'
import { router } from './router'
createApp(App).use(router).mount('#app')
To use add the global variable using Vue 3 and the vue-cli or Vite:
import { createApp } from 'vue'
import { App } from './App.vue'
import { router } from './router'
// 1. Assign app to a variable
let app = createApp(App)
// 2. Assign the global variable before mounting
app.config.globalProperties.globalVar = 'globalVar'
// 3. Use router and mount app
app.use(router).mount('#app')
Then to access the variables in components like this:
<script>
export default {
data() {
return {
myVar: this.globalVar
}
}
}
</script>
like in the template like this:
<template>
<h1>{{ globalVar }}</h1>
</template>
And that's it. Happy coding!
About Global Variables and Composition API
According to the very bottom of samayo's answer on this post, global variables are only available on the Options API.
Quoting the bottom of his answer:
Note: This is only for the Options API. Evan You (Vue creator) says: "config.globalProperties are meant as an escape hatch for replicating the behavior of Vue.prototype. In setup functions, simply import what you need or explicitly use provide/inject to expose properties to app.
I recommend to use provide/inject approach as follows :
in main.js :
import {createApp} from 'vue'
let app=createApp({
provide:{
globalVariable:123
}
}).$mount('#app')
in some child or grand-child component do :
export default{
name:'some-compo',
inject:['globalVariable'],
//then access this.globalVariable as property in you component
...
}
for composition api and script setup :
import { inject } from 'vue'
let globalVar=inject('globalVariable')
If possible you should use imports or provide/inject. Another way to define global variables/functions and use them would be using globalProperties (although this seems to be considered more of an anti-pattern). But if a library you use uses globalProperties then you can use it like this. This also works with global functions.
const app = Vue.createApp({})
app.config.globalProperties.$http = () => {} // global function
app.config.globalProperties.$globalVariable = 'Jimmy' // global variable
1. Using options API
mounted() {
console.log(this.$globalVariable)
}
2. Using setup method
<script setup>
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const progressBar = app.appContext.config.globalProperties.$globalVariable
console.log(this.$globalVariable)
</script>
For those of you who are confused about how to access globalProperties in the setup() method, you can use getCurrentInstance() as in the following documentation.
https://v3.vuejs.org/api/composition-api.html#getcurrentinstance
In my case I had to create a global var and get the data from a script.
Used provide and inject:
In main.js:
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App);
app.provide('message',document.querySelector('script[name="nameSCRIPT"]').innerHTML.split('=').slice(1).join('=').slice(1,-1));
app.mount('#app')
In index.html:
<script name="nameSCRIPT">nameSCRIPT="HELLO"</script>
In child component:
inject:['message'],
mounted(){
console.log(this.message)
},

Call a Vue method from outside the Vue app (Vue Webpack CLI)

I need to call a method from outside my app. I am using the Vue CLI. My method is in a component and looks something like this:
export default {
name: 'home',
...
methods: {
theMethodINeedToCall() {
// does stuff
}
}
I have been searching for a way to access theMethodINeedToCall(). It's not hard to get there when you're not using the Vue CLI, but with the CLI, I can't seem to find my way there.
If the method has nothing to do with the component it's better you host the method outside of the component
At last component is just an object ,just import the component where yo want and call the method;
import MyComponent from './path/'
MyComponent.methods.theMethodINeedToCall();

Categories

Resources