I'm trying to switch to using Vuex instead of my homegrown store object, and I must say I'm not finding the docs as clear as elsewhere in the Vue.js world. Let's say I have a Vuex module called 'products', with its own state, mutations, getters, etc. How do I reference an action in that module called, say, 'clearWorking Data'? The docs give this example of accessing a module's state:
store.state.a // -> moduleA's state
But nothing I can see about getters, mutations, actions, etc.
In Addition to the accepted answer I wanna provide you with a workarround for the getter which is missing in the answer.
Debug the Store
In any case you can call console.log(this.$store) to debug the Store.
If you do so you will see the getters are prefixed with the namespace in their name.
Access namespaced getter
this.$store.getters['yourModuleName/someGetterMethod']
Dispatch namespaced
this.$store.dispatch('yourModuleName/doSomething')
Dispatch namespaced with params
this.$store.getters['yourModuleName/someGetterMethod'](myParam)
Conclusion
The key is to handle the namespace like a file System like Justin explained.
Edit: found a nice library for handling vuex Store
In addition to the basic knowledge I'd like to add this vuex library as a nice addition for working effectivly and fast with the vuex store. https://github.com/davestewart/vuex-pathify .
It looks pretty interesting and cares much of the configuration for you and also allows you to handle 2waybinding directly with vuex.
** Edit: Thanks to the other Answers. Added Dispatching method with params for wholeness.
In your example it would be store.dispatch('products/clearWorkingData') you can think of actions/mutations as a file system in a way. The deeper the modules are nested the deeper in the tree they are.
so you could go store.commit('first/second/third/method') if you had a tree that was three levels deep.
As another addition to the accepted answer, if you need to pass parameter(s) to the getter (for instance to fetch a specific item from the store collection), you need to pass it as follows:
this.$store.getters['yourModuleName/someGetterMethod'](myParam)
I don't think I like this notation very much, but it is what it is - at least for the moment.
Try this approach!
getCounter(){
return this.$store.getters['auth/getToken'];
}
auth is my module name and getToken is my getter.
Using Vuex mapGetters and mapActions you can now do this pretty easily. But I agree, it still isn't very obvious in the documentation.
Assuming your store module 'products' has a getter called 'mostPopular' and an action called 'clearWorkingData':
<template>
<div>
<p>{{mostPopularProduct}}<p>
<p><button #click="clearProductData">Clear data</button></p>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
computed: mapGetters({
mostPopularProduct: "products/mostPopular"
}),
methods: mapActions({
clearProductData: "products/clearWorkingData"
})
}
</script>
The mapGetters helper simply maps store getters to local computed properties:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// mix the getters into computed with object spread operator
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
If you want to map a getter to a different name, use an object:
...mapGetters({
// map `this.doneCount` to `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
You have to be aware of using namespaced: true when configuring particular store object
In Addition to the accepted answer, I feel it's not a good idea to mutate the state and commit the mutation directly in component. Thumb rule I follow is, Always use an action to commit the mutation and state only mutate inside mutations.
Use getters to get a transformed state.
Getters can be mapped to computed using mapGetters and actions can be mapped to methods using mapActions as below example
// component.vue
// namespace_name=products
<template>
<div>
<p> This is awesome product {{getRecentProduct}} </p>
</div>
<button #click="onButtonClick()"> Clear recent</button>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
computed: {
...mapGetters({
getRecentProduct: "products/getRecentProduct",
}),
anotherComputed(){
return "anotherOne"
}
},
methods: {
...mapActions({
clearRecentProduct: "products/clearRecentProduct",
}),
onButtonClick(){
this.clearRecentProduct(); /// Dispatch the action
},
anotherMethods(){
console.log(this.getRecentProduct); // Access computed props
}
}
};
</script>
Here's how you can access vuex Getters & Mutations using Composition API (setup)
<script setup>
import { useStore } from 'vuex'
const store = useStore();
var value = store.getters['subModuleName/getterMethod'];
store.commit['subModuleName/MutationMethod'];
</script>
Related
Trying to implement the new Vue composition API I ran into an issue, my vuex state values are not working and it will return an $store undefined error. When I check if my language state is set it is set so theres data present. I could not find any good information about this but I did find some helper plugin but I dont want to use plugins for every issue so is there a way to do this?
I am using vue 3 with vuex 4
export default {
setup (props, context) {
console.log(context.root.$store.getters['i18n/language'])//not working
// more logic here
}
}
You should use the composable function called useStore to get the store instance :
import {useStore} from 'vuex'
export default {
setup (props, context) {
const store=useStore()
console.log(store.getters['i18n/language'])
}
}
I am attempting to build a Vue.js application that utilizes Vuexfire in the store.js file. My store.js file looks something like this:
import Vue from 'vue'
import Vuex from 'vuex'
import { vuexfireMutations, firestoreAction } from 'vuexfire'
import { db } from '#/main'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
items: []
},
mutations: vuexfireMutations,
actions: {
setEvents: firestoreAction(context => {
return context.bindFirestoreRef('items', db.collection('dbItems'))
}),
},
})
I understand from the documentation that the .bindFirestoreRef() method is what binds Firestore to the Vuex store, while firestoreAction is a wrapper that injects .bindFirestoreRef() into the context object, so that it can be used as a method of the store (along the same lines as the commit method). The payload is then committed to mutations, while vuexfireMutations apparently handles the mutation of the state behind the scenes. Does this mean that vuexfireMutations is a method? What is the right term to describe vuexfireMutations? I really don't feel like the documentation explains very well what this imported item is. I'm just kind of having to guess that it is something that handles mutations, but what is the right term for it?
While reading Vuex repository docs, I came across the following syntax:
export default {
template: '...',
data () { ... },
// NOTICE SYNTAX BELLOW
vuex: {
getters: {
count: function (state) {
return state.count
}
}
}
}
Notice the syntax of vuex option block of the component.
When referencing to either the official Vuex docs or official Vue 2 API docs, the usage of vuex component option, smiliar to the one above, is not mentioned.
The only thing I understand about this block is (according to Vuex repository docs):
Note the special vuex option block. This is where we specify what state the component will be using from the store.
What is the actual usage of vuex block? can it be used instead of component binding helpers? such as mapGetters and mapState?
Seems like the official docs are lack of docs about this feature.
I'd like to have further information about this feature, thank you.
It think it is pointing out the case that one can individually decide on a component basis whether vuex shall be used or not.
The initial example in the vuex docs injects the store on a global level into all Vue instances, meaning that all components have access to the store.
If you'd like to have more control over which component uses vuex you can go for a explicit declaration of using vuex - for each component individually by using the syntax you are referring to.
my files looks like this
./components/UserCreate.vue
./components/UserList.vue
./main.js
my vue instance in main.js
new Vue({
el: '#user-operations',
components: {
CreateUser,
UserList
}
});
index.html
<div id="user-operations">
<create-user></create-user>
<user-list></user-list>
</div
I want to trigger userList() method in UserList.vue when createUser() method in CreateUser.vue is triggered. Or how can i pass last_user property to UserList component from CreateUser.vue for append.
here is my create-user component [working]
https://jsfiddle.net/epp9y6vs/
here is my user-list component [working]
https://jsfiddle.net/ore3unws/
so i want the last user to be listed when createUser() is triggered
I recommend to create a service with all the methods operating user entities. It will separate your Components from the implementaton of the logic which is good because:
The Components doesn't have to know which calls they have to do to servers to retrieve data - it's better to use abstraction level
The Component will be lighter and easier for reuse
You will be able to use this logic (from the service) in several Components - exactly your problem
You have several ways to implement services:
Stateless service: then you should use mixins
Statefull service: use Vuex
Export service and import from a vue code
any javascript global object
I prefer (4). Here is an example how to do it:
In file /services/UsersService that will describe your service put all the relevant methods and expose them with export:
import axios from 'axios'
export default {
get() {
return axios.get('/api/posts)
}
}
Then in any Component that needs this methods import this service:
import UsersService from '../services/UsersService'
export default {
data() {
return {
items: []
}
},
created() {
this.fetchUsers()
},
methods: {
fetchUsers() {
return UsersService.get()
.then(response => {
this.items = response.data
})
}
}
}
Find even more about it in this question:
What's the equivalent of Angular Service in VueJS?
This solution is much better than using this.$parent.$refs.userList which suppose that this components will always stay "brothers" (will have the same parent).
You can trigger userList in CreateUser.vue by this code:
this.$parent.$refs.userList.userList()
and change your index.html:
<div id="user-operations">
<create-user></create-user>
<user-list :ref="userList"></user-list>
</div>
You can create last_user property in main.js then pass it to 2 components:
.
<div id="user-operations">
<create-user:lastUser="last_user"></create-user>
<user-list :lastUser="last_user"></user-list>
</div>
I have made vuex namespaced getter mapping in my .vue component like this:
...mapGetters([
'fooModule/barGetter'
])
How do I access this getter in the .vue component template?
I have tried {{fooModule.barGetter}} but it doesn't seem to work, {{fooModule/barGetter}} is obviously wrong.
I could assign another key to the getter in mapGetters by
...mapGetters({
fooBarGetter: 'fooModule/barGetter'
})
This allows me to access the value in the template by using {{forBarGetter}}
However, I am wondering if there is a way to access the 'fooModule/barGetter' without assigning it another key. Is it possible? if so how?
The first parameter of mapGetters can be a namespace:
computed: {
...mapGetters("fooModule", [
"barGetter"
]),
...mapGetters("anotherModule", [
"someGetter"
])
}
That will make the value available as this.barGetter or just barGetter in templates. Note, it's perfectly acceptable to have multiple mapGetters statements.
Vuex Getters documentation
Vuex Binding helpers with namespace
Well actually it's registered under the key 'fooModule/barGetter', which is not a valid name for a javascript variable. So you should access the key as a string, ah, and so, it's not so elegant. But you can still do it, access it in the template with {{ _self['fooModule/barGetter'] }}.
See this working example:
new Vue({
el: '#app',
store: new Vuex.Store({
modules: {
fooModule: {
namespaced: true,
state: {},
getters: {
barGetter() {
return 'Hi :)';
}
}
}
}
}),
computed: Vuex.mapGetters([
'fooModule/barGetter'
])
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.1/vue.min.js"></script>
<script src="https://unpkg.com/vuex#2.3.1"></script>
<div id="app">
{{ _self['fooModule/barGetter'] }}
</div>
For anyone who wants to achieve this without specifying the namespace as a first parameter, there's also a possibility to pass object/map to mapGetters with already namespaced names of the getters.
...mapGetters({
'activeItems': ACTIVE_ITEMS_GETTER_WITH_NAMESPACE
})
This is extremely useful when you have constants with namespaced names of mutations, getters and actions. In our case, we have lots of modules and it sometimes time consuming to look up the module our getter, mutation or action is inside. This is why we add namespace to our constants.
this.$store.getters.productName
in template, ex:
<vs-td width="20%">{{$store.getters.productName}}</vs-td>