Vue component Vuex option block feature - vue.js

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.

Related

How to access Vuex state in a seperate non-Vue component, JavaScript/TypeScript file?

Something we would like to do is keep as much business logic away from UI logic. Particularly...like to setup a service type Typescript class that can interact with some Node Module API and update the apps state in Vuex.
Rather than having Vue components directly worry about this business logic.
Is there a way I can interact with Vuex using this vuex-class library decorators in a separate just .ts/.js file? So like creating some ES class, and having a store, getter, mutations, etc in it to interact with my Vuex state?
//fooService.ts .......or something like that
import ...
export default class FooService {
#FooModule.State
private FooModel!: FooModel;
#FooModule.Getter
private foo!: Foo;
#FooModule.Mutation
private setFoo!: (newFoo: Foo) => void;
private doFooBusinessLogic(){ ... }
}
And then in the Vue component just initializing this class and calling it to interact with this Vuex data.
Rather than having to do ALL interactions with Vuex within a Vue component.
I was able to accomplish this by using this library instead, https://github.com/gertqin/vuex-class-modules
The README describes it well...
import { userModule } from "path/to/user-module.ts";
And then you use the Vuex module mutations etc with userModule.someMutation()

Vue/Nuxt: How to make a component be truly dynamic?

In order to use a dynamically-defined single page component, we use the component tag, thusly:
<component v-bind:is="componentName" :prop="someProperty"/>
...
import DynamicComponent from '#/components/DynamicComponent.vue';
...
components: {
DynamicComponent
},
props: {
componentName: String,
someProperty: null,
}
The problem is, this isn't really very dynamic at all, since every component we could ever possibly want to use here needs to be not only imported statically, but also registered in components.
We tried doing this, in order at least to avoid the need to import everything:
created() {
import(`#/components/${this.componentName}.vue`);
},
but of course this fails, as it seems that DynamicComponent must be defined before reaching created().
How can we use a component that is truly dynamic, i.e. imported and registered at runtime, given only its name?
From the documentation: Emphasis mine
<!-- Component changes when currentTabComponent changes -->
<component v-bind:is="currentTabComponent"></component>
In the example above, currentTabComponent can contain either:
the name of a registered component,
or a component’s options object
If currentTabComponent is a data property of your component you can simply import the component definition and directly pass it to the component tag without having to define it on the current template.
Here is an example where the component content will change if you click on the Vue logo.
Like this:
<component :is="dynamic" />
...
setComponentName() {
this.dynamic = () => import(`#/components/${this.componentName}.vue`);
},
Solution for Nuxt only
As of now its possible to auto-import components in Nuxt (nuxt/components). If you do so, you have a bunch of components ready to be registered whenever you use them in your vue template e.g.:
<MyComponent some-property="some-value" />
If you want to have truly dynamic components combined with nuxt/components you can make use of the way Nuxt prepares the components automagically. I created a package which enables dynamic components for auto-imported components (you can check it out here: #blokwise/dynamic).
Long story short: with the package you are able to dynamically import your components like this:
<NuxtDynamic :name="componentName" some-property="some-value" />
Where componentName might be 'MyComponent'. The name can either be statically stored in a variable or even be dynamically created through some API call to your backend / CMS.
If you are interested in how the underlying magic works you can checkout this article: Crank up auto import for dynamic Nuxt.js components
According to the official Documentation: Starting from v2.13, Nuxt can auto import your components when used in your templates, to activate this feature, set components: true in your configuration
you are talking about async components. You simply need to use the following syntax to return the component definition with a promise.
Vue.component('componentName', function (resolve, reject) {
requestTemplate().then(function (response) {
// Pass the component definition to the resolve callback
resolve({
template: response
})
});
})

Where to import file js in vue/nuxt

Usually I use a js code with functions to run on some events.
Now I am using nuxt.js and I wonder where to put this file or how to create a global method to use these functions in every component.
I could write the methods that I need inside every a specific component but after it wouldn't be usable outsite of it.
How to do that in vue/nuxt?
So one way to do it in vue.js is by using mixins, in nuxt you can also use mixins, then you should register them as plugins, but first:
Non global mixins
Create an extra folder for your mixins. For example in a /mixins/myMixin.js
export default {
methods: {
commonMethod() {
console.log('Hello')
}
}
}
Then import in a layout, page or component and add it via the mixins object:
<script>
import myMixin from '~/mixins/myMixin.js'
export default {
mixins: [myMixin]
}
</script>
Global mixins
For example in a new file plugins/mixinCommon.js:
import Vue from 'vue'
Vue.mixin({
methods: {
commonMethod() {}
}
})
Include the file in nuxt.config.js like that:
plugins: ['~/plugins/mixinCommon']
After that you would have the method everywhere available and call it there with this.commonMethod(). But here an advice from the vue.js docs:
Use global mixins sparsely and carefully, because it affects every
single Vue instance created, including third party components. In most
cases, you should only use it for custom option handling like
demonstrated in the example above. It’s also a good idea to ship them
as Plugins to avoid duplicate application.

Need multiple instances of Vuex module for multiple Vue instances

I'm integrating Vue onto a site for forms, which means I have to create several instances of that Vue application if there are multiple forms on the page. All instances share the same Vuex store.
I created a Vuex module so that each Vue instance could have it's own local state. My main goal is to prevent one Vue instance from updating the state of another Vue instance.
Here's my Vuex module
export default {
state() {
return {
stateObj: {...}
}
}
}
Creating my Vuex instance:
export default new Vuex.Store({
modules: {
form
}
})
I was reading the Vuex docs and it says you need to use a function that returns the modules state, which is what I'm doing. However, when I update the module's state in one my Vue instances, it updates the all other Vue instance's module state.
Here's the section in the Vuex docs I am referring to.
The reason this is happening is that you are not really creating multiple instances of form module or your store. You are essentially creating multiple instances of Vue applications, that is you are having multiple Root Vue Instances.
However, your store code clearly suggests that you are creating a single instance of Vuex store by exporting an instance like: export default new Vuex.Store({ /**/ }).
If you have multiple root instances i.e. multiple Vue applications, then you need multiple instances of the Vuex Store. Your code would be something like:
// store.js - Note: Do not return singleton store
export default function makeStore() {
return new Vuex.Store({
modules: { form }
});
}
// main.js/root.js
import makeStore from './store.js';
// Creating multiple instances of root instance and Vuex store.
const app1 = new Vue({ store: makeStore() });
const app2 = new Vue({ store: makeStore() });
This should solve your problem. Though this design is not uncommon, you should still take a step back and think - what if you need to share data across all these apps? Since there are multiple instances, you cannot do that easily. As long as all these forms are working in isolation, it should be fine.
Alternately, if you have to really make single instance of your store, then consider changing your store design. You should have a single root instance/app and use v-for to generate multiple forms. Similarly, on the store side, you would have an array to maintain a collection of all the forms.

How to access Vuex module getters and mutations?

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>