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
Related
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.
Is it possible to use a mixin imported from a VueJS plugin in one component?
I've created a plugin and when I import it I can access the mixin's functions from all my components.
Is there a way to make it available in only one component ? or all the plugin add global-level functionality to Vue by definition?
IMHO you should use create 2 things:
the plugin that imports the essentials globally
the mixin that needs to be imported in the components you want
example:
//main.js
import {MyPlugin} from 'my-library'
vue.use(MyPlugin)
in the component
//component.vue
import {MyMixin} from 'my-library'
export default {
mixins: [myMixin],
}
You can register a mixin either globally, either locally. If you don't register a mixin globally, it will be only available in components where it is locally registered. So, with local registration, you can make a mixin available in only specific components.
Registering globally: you just need to declare it in the main.js file
Nb: you don't need to register the mixin in components
Vue 2:
// main.js
import myMixin from './mixins/myMixin'
Vue.mixin(myMixin) // makes the plugin globally available
new Vue({
// ...
}).$mount('#app')
Vue 3:
// main.js
import myMixin from './mixins/myMixin'
const app = createApp(App)
app.mixin(myMixin) // makes the plugin globally available
app.mount('#app')
Registering locally: you do NOT declare it in the main.js file, and you register the mixin in the relevant components
// componentWithMixin.vue
import myMixins from "../mixins/myMixin"
export default {
// ...
mixins: [myMixins] // importing the mixin - without this line, the mixin can't be available
}
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)
},
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();
In my angular app I use angular-redux for application state management. In my main module I defined my redux store. Like this:
export class MainModule {
constructor(private ngRedux: NgRedux<MainAppState>,
private devTools: DevToolsExtension) {
let enhancers = [];
if (environment.production === false && devTools.isEnabled()) {
enhancers = [...enhancers, devTools.enhancer()];
}
this.ngRedux.configureStore(
reducer,
{} as MainAppState,
[],
enhancers);
}
}
I created new child module, which contains some components. These components should access to application state. In one of these components I access via #select to store, but this doesn't work. Here is how I access to store:
export function getLanguage(state: LanguageState) { return state.userLanguage; }
And this code I have in my ChildComponent class:
export class ChildComponent implements OnInit {
#select(getLanguage) savedUserLanguage$: Observable<LanguageState>;
// more code
}
How can I access to application state store from child modules? What should I import in child module? Will It be better to create own module only for redux store handling? Maybe I forgot something?
I use Angular v4 and #angular-redux/store v6.
I'd recommend creating a separate module that just contains your store, e.g. StoreModule. You can then import your StoreModule into all your child modules and access your store from there.
This is the way they go in the official example app:
StoreModule: https://github.com/angular-redux/example-app/blob/master/src/app/store/module.ts
Child Module: https://github.com/angular-redux/example-app/blob/master/src/app/elephants/module.ts
Component in child module: https://github.com/angular-redux/example-app/blob/master/src/app/elephants/page.ts
I was thinking about refactoring some ugly old JavaScript code that uses prototypal inheritance into an Angular 7+ project. I was asking myself pretty much the same question. Inspired by my udemy Angular course, I tried an experiment with a ngrx store and lazy loaded modules.
(Keep in mind that ngrx is SIMILAR to #angular-redux, but it's NOT the same thing. See https://ngrx.io/docs for details.)
Here it is.
I create the store in the main module with StoreModule.forRoot and in each lazy loaded module, I create a reference to the store with StoreModule.forFeature.
(See https://ngrx.io/api/store/StoreModule for details.)
When I dispatch actions on the store with the lazy loaded components, those actions (and corresponding reducers) seem to change the value to which the main app component subscribes.
Also, when I dispatch actions on the store with the main app component, those actions (and corresponding reducers) seem to change the value to which the lazy loaded components subscribe.
Also, it's hard to explain what I did in a simple 200-500 character block so I had to use a github project.