I have a Vuex getter that I call from various components in my application. However, I have found a case were slightly more complex logic is required before calling the getter, so I am making use of a Vuex Action. How can I call the getter function with a parameter from my action?
I use constants for naming getters/mutations/actions, so my getter is defined as follows: [GETTER_NAME]: state => param => { return {/.../} }. In my Vuex action, I would like to call the getter as follows getters[GETTER_NAME](someParam). However, this does not seem to work (even though getters[GETTER_NAME] returns a function).
Calling the getter from a component works perfectly fine. I simply create computed function and use ...mapGetters({getterName: GETTER_NAME}). To call the getter with a parameter I just say getterName(someParam).
[GETTER_NAME]: state => param=> {
return {/.../}
},
[ACTION_NAME]: (context, param) => {
getters[GETTER_NAME](param)
? context.commit(MUTATION_X, param)
: context.commit(MUTATION_Y, param);
}
The getter gets called, however, it returns the function without passing in the parameter. Am I doing something wrong or am I misunderstanding the way getters work in Vuex?
You need to call like context.getters[GETTER_NAME](someParam) inside actions here.
[GETTER_NAME]: state => param=> {
return {/.../}
},
[ACTION_NAME]: (context, param) => {
context.getters[GETTER_NAME](param)
? context.commit(MUTATION_X, param)
: context.commit(MUTATION_Y, param);
}
In actions have injected parameters : dispatch, commit, getters and rootState. Therefore you can access getters like this:
ACTION_NAME: ({ commit, getters }, payload) => {
let MY_VARIABLE = getters.GETTER_NAME(payload)
console.log(MY_VARIABLE)
}
This works fine even if you try to access a getter from a different module.
Though you can use getters with context via context.getters the syntax gets a bit longish inside the action when using it this way.
Related
I want to access the props inside the async fetch() but I'm also using async fetch(context). So, I'm not sure how to access the props.
In Nuxt 2, you have 2 fetch hooks.
The old one, before Nuxt 2.12, fetch(context) which acts a lot like asyncData. It's executed before the component creation, so you don't have access to it (data, props, options... nothing).
This one is deprecated, use asyncData instead.
The new one, from Nuxt 2.12, fetch() (with no parameters). It's executed at the same time as the created() hook. It has access to the component's context (props, data, etc.).
fetch(context) {
// "this" doesn't exists
// context is the Vue global context
}
fetch() {
this.myProp // "this" exists and have access to props
}
As shown in the documentation, you can access the context with this when using the fetch() hook like
async fetch() {
await this.$axios // or whatever this.$i18n etc.....
}
Don't use fetch(context) as explained here.
tl;dr
How to access other getters from a parameterized getter?
Normally, you can use this.myGetter; but a parameterized getter is implemented as an arrow function, wherein this is undefined.
What's the preferred way to handle this case in Pinia?
I'd like to create a parameterized getter (multiSum) in my Pinia store, which accesses another getter (sum).
Getters can be accessed via this, but that won't work from within an arrow function which is used to implemented a parameterized getter: multiSum crashes because this is undefined in the context of the nested arrow function.
getters: {
sum: (state) => state.a + state.b,
multiSum: (state) => (count) => this.sum * count // crash: this is undefined
}
In Vuex, parameterized getters can access other getters via a parameter instead of this, which also works in arrow functions. But afaik this API does not existing in Pinia.
I can work around this by capturing the store instance:
multiSum(state) {
const store = this
return (count) => store.sum * count
}
This works, but is pretty verbose. Is there a better (more framework-compliant) way to do this?
this can be undefined inside arrow function because it's not interchangeable with regular function and gets a context from parent scope.
The usage of this and state is explained in details in the documentation. Either this or state can be used, but this has better Typescript and thus IDE support.
In the first snippet, state is already available in function scope, there is no need to access this:
multiSum: (state) => (count) => state.sum * count
In the second snippet, const store = this isn't needed because a store is already this.
multiSum() {
return (count) => this.sum * count
}
I'm new to Vue and I'd like to make an AJAX call every time my component is rendered.
I have a vue component lets say "test-table" and Id like to fetch the contents via an AJAX call. There are many such tables and I track the active one via an v-if/v-else-if etc.
Currently I have a cheaty solution: in the template for the component I call a computed property called getData via {{ getData }} which initiates the Ajax call but does only return an empty string. Id like to switch to the proper way but dont know how.
My code is like so: (its typescript)
Vue.component("test-table", {
props: ["request"],
data () {
return {
tableData: [] as Array<TableClass>,
}
},
template: `{{ getData() }} DO SOME STUFF WITH tableData...`,
computed: {
getData() : string {
get("./foo.php", this.request, true).then(
data => this.tableData = data.map(element => new TableClass(data))
)
return "";
}
}
}
HTML:
<test-table v-if="testcounter === 1" :request="stuff...">
<test-table v-else-if="testcounter === 2" :request="other stuff...">
...
get is an async method that just sends a GET request with request data to the server. The last parameter is only for saying the method to expect a JSON as answer. Similar to JQuerys getJSON method.
the "created" method does NOT work! It fires only one time when the component is first created. If I deactivate and activate again (with v-if) the method is not called again.
Btw: I'm using Vue 2.6.13
Lifecycle hooks won't fire every time if the component is cached, keep-alive etc
Add a console.log in each of the lifecycle hooks to see.
Change to use a watcher which handles firing getData again if request changes.
...
watch: {
request: {
handler: function() {
this.getData()
},
deep: true
}
},
created() {
this.getData()
},
methods: {
getData(): string {
// do request
}
}
#FlorianBecker try the lifecycle hook updated(). It may be a better fit for what you're trying to achieve. Docs here.
You should be able to use the mounted hook if your component is continuously rendered/unrendered using v-if, like so:
export default {
mounted() {
// do ajax call here
this.callAMethod();
},
...
}
Alternatively, you could use the created() hook but it is executed earlier in the chain, so this means the DOM template is not created yet so you cant refer to it. mounted usually is the way to go.
More info on these hooks can be found here.
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.
Using redux, I am trying to write a "helper function" in my redux module which returns filtered data from state based on what index i pass into it. This data is used to build out a form of inputs based on whether auth: true or budt: true. The form basically would iterate over all totypes for that level and conditionally show/hide the auth or budt inputs.
Given the state...
totypesmap: [
{
level:1,
totypes:[
{ttno:1, ttcode:'', ttdesc:'regular', auth:true, budt:false},
]
}
]
I have a function exported in my module that expects an index argument and returns its appropriate totypesmap[index] from state. However in order to get to state from within it i have to use getState() which expects a Promise to be resolved with its data I am assuming.
Should i be accomplishing this some other way?
export const MyTOTypes = (level) => {
return (dispatch, getState) => {
const state = getState()
// return state.masterdata.totypesmap[level].totypes
}
}
In my container or component I simply import { MyTOTypes } from 'redux/modules/masterdata' and then call it whenever i need to know the TO Types assigned to my "level" MyTOTypes(0)
If the data is constant, don't put it in the redux store. Just put it in a module and expose your helper methods to retrieve the data.
const totypesmap = [ ... ];
export function MyToTypes(level) { return totypesmap[level].totypes; }
If you really want it in the Redux store, then have your method take state as the first argument. Your React components will be calling this method from within their mapStateToProps method and will have the store state available:
export function MyToTypes(state, level) { return state.totypesmap[level].totypes; }