(Vue) Inject variable into all components [duplicate] - javascript

I have a javascript variable which I want to pass globally to Vue components upon instantiation thus either each registered component has it as a property or it can be accessed globally.
Note:: I need to set this global variable for vuejs as a READ ONLY property

Just Adding Instance Properties
vue2
For example, all components can access a global appName, you just write one line code:
Vue.prototype.$appName = 'My App'
Define that in your app.js file and IF you use the $ sign be sure to use it in your template as well: {{ $appName }}
vue3
app.config.globalProperties.$http = axios.create({ /* ... */ })
$ isn't magic, it's a convention Vue uses for properties that are available to all instances.
Alternatively, you can write a plugin that includes all global methods or properties. See the other answers as well and find the solution that suits best to your requirements (mixin, store, ...)

You can use a Global Mixin to affect every Vue instance. You can add data to this mixin, making a value/values available to all vue components.
To make that value Read Only, you can use the method described in this Stack Overflow answer.
Here is an example:
// This is a global mixin, it is applied to every vue instance.
// Mixins must be instantiated *before* your call to new Vue(...)
Vue.mixin({
data: function() {
return {
get globalReadOnlyProperty() {
return "Can't change me!";
}
}
}
})
Vue.component('child', {
template: "<div>In Child: {{globalReadOnlyProperty}}</div>"
});
new Vue({
el: '#app',
created: function() {
this.globalReadOnlyProperty = "This won't change it";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="app">
In Root: {{globalReadOnlyProperty}}
<child></child>
</div>

In VueJS 3 with createApp() you can use app.config.globalProperties
Like this:
const app = createApp(App);
app.config.globalProperties.foo = 'bar';
app.use(store).use(router).mount('#app');
and call your variable like this:
app.component('child-component', {
mounted() {
console.log(this.foo) // 'bar'
}
})
doc: https://v3.vuejs.org/api/application-config.html#warnhandler
If your data is reactive, you may want to use VueX.

You can use mixin and change var in something like this.
// This is a global mixin, it is applied to every vue instance
Vue.mixin({
data: function() {
return {
globalVar:'global'
}
}
})
Vue.component('child', {
template: "<div>In Child: {{globalVar}}</div>"
});
new Vue({
el: '#app',
created: function() {
this.globalVar = "It's will change global var";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div id="app">
In Root: {{globalVar}}
<child></child>
</div>

If the global variable should not be written to by anything, including Vuejs, you can use Object.freeze to freeze your object. Adding it to Vue's viewmodel won't unfreeze it.
Another option is to provide Vuejs with a frozen copy of the object, if the object is intended to be written globally but just not by Vue: var frozenCopy = Object.freeze(Object.assign({}, globalObject))

you can use Vuex to handle all your global data

In your main.js file, you have to import Vue like this :
import Vue from 'vue'
Then you have to declare your global variable in the main.js file like this :
Vue.prototype.$actionButton = 'Not Approved'
If you want to change the value of the global variable from another component, you can do it like this :
Vue.prototype.$actionButton = 'approved'
https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#Base-Example

If you’d like to use a variable in many components, but you don’t want to pollute the global scope. In these cases, you can make them available to each Vue instance by defining them on the Vue prototype:
Vue.prototype.$yourVariable = 'Your Variable'
Please remember to add this line before creating your Vue instance in your project entry point, most of time it's main.js
Now $yourVariable is available on all Vue instances, even before creation. If we run:
new Vue({
beforeCreate: function() {
console.log(this.$yourVariable)
}
})
Then "Your Variable" will be logged to the console!
doc: https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#Base-Example
If you want to make this variable immutable, you can use the static method Object.defineProperty():
Object.defineProperty(Vue.prototype, '$yourVariable', {
get() {
return "Your immutable variable"
}
})
This method by default will prevent your variable from being removed or replaced from the Vue prototype
If you want to take it a step further, let's say your variable is an object, and you don't want any changes applied to your object, you can use Object.freeze():
Object.defineProperty(Vue.prototype, '$yourVariable', {
get() {
return Object.freeze(yourGlobalImmutableObject)
}
})

A possibility is to declare the variable at the index.html because it is really global. It can be done adding a javascript method to return the value of the variable, and it will be READ ONLY. I did like that:
Supposing that I have 2 global variables (var1 and var2). Just add to the index.html header this code:
<script>
function getVar1() {
return 123;
}
function getVar2() {
return 456;
}
function getGlobal(varName) {
switch (varName) {
case 'var1': return 123;
case 'var2': return 456;
// ...
default: return 'unknown'
}
}
</script>
It's possible to do a method for each variable or use one single method with a parameter.
This solution works between different vuejs mixins, it a really global value.

in main.js (or any other js file)
export const variale ='someting'
in app.vue (or any other component)
import {key} from '../main.js' (file location)
define the key to a variable in data method and use it.

Simply define it in vite configuration
export default defineConfig({
root:'/var/www/html/a1.biz/admin',
define: {
appSubURL: JSON.stringify('/admin')
}, ..../// your other configurations
});
Now appSubURL will be accessible everywhere

Related

Vue 3 composition API and access to Vue instance

In main.js I have something like this:
import { myUtilFunc} from './helpers';
Object.defineProperty(Vue.prototype, '$myUtilFunc', { value: myUtilFunc });
In this way I have acess to myUtilFunc across whole application with this.$myUtilFunc
But how can I achieve the same in setup() method in Vue 3 if I don't have access to this?
Use provide/inject
Provide
const app = createApp(App);
app.provide('someVarName', someVar); // `Provide` a variable to all components here
Inject:
// In *any* component
const { inject } = Vue;
...
setup() {
const someVar = inject('someVarName'); // injecting variable in setup
}
Note that you don't have to provide from the app root, but can also provide from any component to only its sub-components:
// In *any* component
setup() {
...
},
provide() {
return {
someVarName: someVar
}
}
Original answer
[Edit: While my original answer below is still useful for context properties, it's no longer recommended to use context.root, which is no longer mentioned in the guide and may soon be deprecated.]
In Vue 3, setup has an optional second argument for context. You can access the Vue instance through context.root instead of this:
setup(props, context) {
context.root.$myUtilFunc // same as `this.$myUtilFunc` in Vue 2
}
Things you can access through context:
context.attrs
context.slots
context.parent
context.root
context.emit
While Dan's answer is correct, I would like to provide an alternative mentioned in the comments to the accepted answer. There are pros and cons to each, so, you need to choose based on your needs.
To understand why the code below works, it is important to remember, that provided properties are transitive in the tree of components. I.e. inject('foo') will look for 'foo' in every parent going up the hierarchy all the way to the app; there is no need to declare anything in the middle wrappers.
So, we can write something like this, where globalDateFormatter() is just an example function we want to use in any component down the tree:
main.js
import { createApp } from 'vue'
import App from './App.vue'
const globalDateFormatter = (date) => {
return '[' + date.toLocaleString() + ']'
}
const app = createApp(App)
app.provide('globalDateFormatter', globalDateFormatter) // <-- define here
app.mount('#app')
And then, in some DeepDownComponent.vue:
<template>
<p> {{ fmt(new Date()) }} </p>
</template>
<script>
import { inject } from 'vue'
export default {
setup(){
const fmt = inject('globalDateFormatter', x => x.toString())
// ^-- use here, optional 2nd parameter is the default
return {fmt}
}
}
</script>
Obviously, you can directly import and use provide and inject with the following signatures
provide<T>(key: InjectionKey<T> | string, value: T): void
and
inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
anywhere in your code, doesn't have to be app.provide()
You can also provide values, even the global store, like this, just don't forget to use ref() or reactive() as needed.
In short, whenever you would prefer dependency injection, provide/inject are your friends.

Making global functions to access vuex store

so I want to make a global function that I can access in every component of mine. So I stumbled upon Vue Plugins. They worked great, till I tried out my use case. I need to use some information from the vuex store in my plugin and return a true or false value.
So this is what I have tried
plugin.js
export default {
install (Vue) {
Vue.prototype.$rbac = (method, route) => {
$store.state.generic.user.routes.some(m => m.method.includes(method) && m.route==route)
}
}
}
main.js
import plugin from './utils/common/plugin'
...
Vue.use(plugin)
...
component.vue
<template>
...
<div v-if="$plug('post', '/project')></div>
...
</template>
But I get an error saying "ReferenceError: $store is not defined".
It kind of makes sense that I cannot access the store. The store only gets the value once the user logs in.
So is there any way I can make a global function that can access the store when it gets values?
You're getting the reference error because the $store variable hasn't been defined anywhere. It's not a local variable, nor is it a function parameter or global variable.
You probably meant to do this.$store; also make sure you use function () {} syntax and not () => {} because you don't want to bind this.
export default {
install(Vue) {
Vue.prototype.$rbac = function (method, route) {
this.$store.state.generic.user.routes.some(m => m.method.includes(method) && m.route == route)
}
}
}
You could also use a global mixin to do a similar thing instead of a plugin.

Vue2 passing arbitrary named variable as prop

I am new to Vue and after checking the docs I can not figure out how to achieve the following:
pass an arbitrarily named variable as a prop to a component instance.
From my understanding, props are meant to be a way to allow data to be passed to a component and as it states on the website:
Passing Data to Child Components with Props:
Props are custom attributes you can register on a component. When a value is passed to a prop attribute, it becomes a property on that component instance.
Since props can be required, it would seem that we can design components under the assumption that some data would be there, and possible within certain parameters (if the validator option is specified).
So I would like to define a function or object outside of vue, e.g. in an application, and pass this function or object to my vue instance.
This works if my named object of function has the exact same name as the prop to which I attempt to bind it. However, as I might have multiple instances of the Vue component and I might want to bind different data, I find using the same name for the variable less than ideal.
Now if I do as the Vue warning suggests, and name object / function the same as the prop, then the warning switches to that my data is not defined inside vue and to make sure it is reactive by reading: https://v2.vuejs.org/v2/guide/components-props.html
which, to be honest, doesnt really explain how to solve the issue,
or move the prop to the data level.
Which I can do (still gives the same warning), but kind of defeats the purpose of having props with my understanding of Vue.
This become more frustrating with anonymous vue instances.
e.g.
<script>
export default {
props: {
// records: {
// default: function(){return{}},
// type: Object
// }
},
data: function() {
return {
records: {} // define even an empty value in data for it to be 'reactive'
}
},
computed: {
fields: function() {
},
keys: function() {
return Object.keys(this.records)
}
},
methods: {
}
}
</script>
trying to use this as a component and set records to var myRecords = {"a": {}} fails:
<my-comp :records="myRecords"/>
So how exactly should I circumvent this? Where should I define my data then? and how should I handle the naming in the case of multiple instances?
A more fledged on example is found on a similar question:
Vue2: passing function as prop triggers warning that prop is already set
So I would like to define a function or object outside of vue, e.g. in an application, and pass this function or object to my vue instance.
It's hard to give a definitive answer because I don't know the specifics of how you have organized your code. Are you using Webpack? Single file components (.vue)? If yes to any of these, then you needn't use global variables in the way you have described in your question.
Your entire Vue app should consist of a single root Vue instance (which you instantiate with new Vue(...), and from there each component is rendered within the root component's template, and templates of those components, and so on.
Looking at the following template:
<my-comp :records="myRecords"/>
myRecords must be a property on the Vue component instance whose template contains the above. It could be declared within the data block, or as a computed property, or a prop, it doesn't matter.
Here's a small example:
<div id="app">
<my-comp :records="myRecords"></my-comp>
</div>
// Obtain records in some way and store it in a global variable
var records = ...
// This is the root Vue instance
new Vue({
el: '#app',
data: {
// You must store the records array in the Vue component like this
// for it to be referenced within the template.
// You can optionally transform the data first if you want.
myRecords: records.filter(r => r.name.startsWith('Bob'))
// ^ ^
// | |
// | +--- the global variable
// |
// +---- the name of the property on the component instance
}
})
Note that MyComp component does not access the records global variable in any way, it only takes its input through the records prop.

How to create local helpers to tidy up Vue components

I have a Vue component that does a number of complex tasks in mounted(). These tasks include for example, initializing Bootstrap Date Pickers, Time Pickers, and Typeaheads.
At the moment all of this initialization code is in my mounted() method. In order to understand what's going on, the developer has to read through the code comments.
Ideally I would move sections of code to their own methods, and only have method calls in mounted(), something such as:
mounted () {
this.helpers.initDatePickers();
this.helpers.initTimePickers();
this.helpers.initTypeaheads();
}
How can I achieve this? I realise that I can put them in the methods object, but I would prefer to leave that for methods which can be accessed via declarations in templates.
Note that I am not asking how to share helper functions across components (or globally). I am merely asking how to create local functions in their own space, in order to clean up some longer methods.
You could create a mixin module which has generic initialization.
// DatePickerMixin.js
import whatever from 'specific-date-picker-stuff';
export default {
methods: {
initDatePickers() {
// initialization here
}
}
}
Then your component just uses the mixin modules.
<script>
import DatePickerMixin from './mixins/DatePickerMixin';
import TimePickersMixin from './mixins/TimePickersMixin';
export default {
mixins: [
DatePickerMixin,
TimePickersMixin
],
data() {/* ... */},
// etc.
}
</script>
You could wrap all of these in the same mixin as well.
And if you don't want to always set the mixins, there's global mixin.
import DatePickerMixin from './mixins/DatePickerMixin';
Vue.mixin(DatePickerMixin);
Use global mixins sparsely and carefully, because it affects every
single Vue instance created, including third party components.
As #EmileBergeron said, mixins are a good solution. You can also create plugins, which so happen to encompass mixins as well but much more. They allow you to extend the Vue constructor or add instances/methods directly to the Vue prototype.
Section on plugins from the documentation
MyPlugin.install = function (Vue, options) {
// 1. add global method or property
Vue.myGlobalMethod = function () {
// something logic ...
}
// 2. add a global asset
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// something logic ...
}
...
})
// 3. inject some component options
Vue.mixin({
created: function () {
// something logic ...
}
...
})
// 4. add an instance method
Vue.prototype.$myMethod = function (methodOptions) {
// something logic ...
}
}
Using your plugin is done by:
// calls `MyPlugin.install(Vue)`
Vue.use(MyPlugin)
// pass options to your plugin
Vue.use(MyPlugin, { someOption: true })
Here's a small plugin I recycle exposing various string functions in the pluralize library:
import {plural, singular, camelCase} from 'pluralize'
PluralizePlugin.install = function (Vue, options) {
Vue.plural = plural
Vue.singular = singular
Vue.camelCase = camelCase
}
With it you can use this.singular(str), this.plural(str), etc. throughout your components. Pretty simple but convenient.

exported method and unexported methods in Vue.js component

I have two functions defined in a component. foo() is defined just within <script>, and fooExported() is defined in the body of export default {}
My understanding is that functions inside export default {} can be accessed in the template, so it sounds the "unexported" function foo() is a "private" function only available within the <script> scope (Is this correct?). What other difference do they have?
Also I'm trying to access this.$data in the "unexported" method but it shows undefined error. Is it not possible to access the data?
<template>
...
</template>
<script>
function foo(){
console.log(this.$data.message) // error: 'this' is undefined.
}
const bar = 123
export default {
data(){
return {
message: 'MyMessage'
}
},
methods: {
fooExported(){
console.log(this.$data.message) // this works.
}
}
}
</script>
<style scoped>
</style>
You are defining a component in a single-file component .vue file. This means that everything inside the default object is passed directly to the constructor method for a new Vue instance. Vue knows to automatically set the reference to this in any method defined within the methods object.
Your foo method is never handled by Vue, and the reference to this does not point to the Vue instance in the context of that function.
If you want your foo method to have a reference to the message data property, you could call the method from the created hook and pass the this.message as a parameter:
created() {
foo(this.message);
}
side note: as you can see above, you can reference data properties directly from this; you don't need to go through this.$data.

Categories

Resources