Making global functions to access vuex store - javascript

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.

Related

TypeError: Cannot set properties of undefined (setting 'message') in Vue

i'm new in Vue.js, so there are concepts that maybe i'm missing.
The thing is that i've been trying to do this for months, but i couldn't get a solution. What i'm trying to do is change the message of a v-alert but on another js script, importing the variable of said message and changing it.
This is a piece of my App.vue
<v-app>
<v-alert transition="scale-transition" :type="success" max-width="280" height="55" class="justify-center">{{alert.message}}</v-alert>
...
I have this declared variables on appController.js
export default{
data () {
return {
alert:{
visible: true,
type: "success",
message: "test"
}
What i'm trying to do is getting that variables on another js script, so i can modify it like this thing i did on loginController.js
import {alert} from './appController.js';
export default {
methods: {
loginFunc() {
//When i call loginFunc, content of var message changes to "Test 2"
alert.message = "Test 2";
}
}
}
But when i call loginFunc from a v-btn, i get these errors on console:
vue.runtime.esm.js?c320:619 [Vue warn]: Error in v-on handler: "TypeError: Cannot set properties of undefined (setting 'message')"
TypeError: Cannot set properties of undefined (setting 'message')
What i am doing wrong? What can i do to solve that and change content of var message, so i can show it on the v-alert?
alert in undefined because appController.js doesn't export alert. Based on the code you shared, it exports a data method which has the alert.
So to access it, it would look something like...
import {data} from './appController.js';
const { alert } = data();
export default {
methods: {
loginFunc() {
alert.message = "Test 2";
}
}
}
This would make it accessible, but not sure if that's what you want.
If you want to be able to edit a value from anywhere you would use a a global store. To make it reactive you could use ref or reactive but only with composition API but also using a global store like vuex, pinia or oeve vue observable. If you have a single alert that watches for that globally-accessible and reactive value, it would then update.
Note that even in your current example, the value appears not to be globally accessible nor reactive, so you would have to manage reactivity and pass it to the component.
If you're using vue3, you can have a look at this sample via vue SFC playground. Note that it's using reactive to manage a "singleton" global state and the alert component is always present, but the visibility is handled internally.

How to use mapState in js files?

I want to access my states from a .js file using mapState in Vue.js.
I've tried
import { mapState } from 'vuex';
const foo = {
...mapState(['axios']),
};
foo.axios.get('...');
but it doesn't work. The error is
TypeError: foo.axios.get is not a function
What should I do to achieve that?
I have searched for other questions, but they access from store.state. ... instead of using mapState which I want.
I'm not sure using mapState is a good idea as it's very much intended to be used as a way to create computed properties on a component.
However, it can be made to work like this:
const foo = {
$store: store,
...mapState(['axios'])
};
foo.axios().get('...');
You can see the implementation of mapState here:
https://github.com/vuejs/vuex/blob/dev/src/helpers.js#L7
Note that it relies on this.$store to get a reference to the store. On a component this will be injected automatically but for your object it needs adding manually.
The other thing to note is that I'm having to invoke axios() as a method. Computed properties on components are defined as functions but accessed as properties but that magic trick is performed internally by Vue. On a normal JavaScript object like this there is no such magic so we just have to call the function as a function. Other benefits of computed properties, such as the caching, will also be lost.

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.

(Vue) Inject variable into all components [duplicate]

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

Trouble accessing props in callback function of Google Login using react-google-login

I'm using react-google-login in my react-redux project and having trouble accessing the props for the component in which this login button exists. I used react-facebook-login in a similar way and it works fine - however, console.log(this) in the loginGoogle() function prints 'undefined' whereas it printed the Javascript object representing the whole Login component in my similar loginFacebook() method. Any ideas as to how I can access this.props in loginGoogle()?
In my Login component:
//all needed import statements
class Login extends Component {
loginGoogle(response) {
console.log(response);
this.props.loginGoogleRequest(response.profileObj.email, response.accessToken, response.tokenObj.expires_in)
}
render() {
<GoogleLogin
clientId="{client id here}"
onSuccess={this.loginGoogle}
className="custom-google-btn"
/>
}
function mapDispatchToProps(dispatch) {
return {
loginGoogleRequest: (email, accessToken, expiresIn) => {
//some code that isn't being reached
}
}
}
export default connect(mapDispatchToProps)(Login);
I trimmed a lot of the fat out of this class in order to make it more readable - please let me know if it would help if I included more code in any way.
Try change loginGoogle definition to Arrow function:
loginGoogle(response) { => loginGoogle = (response) => {
An arrow function does not create its own "this", the this value of the enclosing execution context is used.
Or you may bind loginGoogle method, refer to this answer:
Why JSX props should not use arrow functions

Categories

Resources