How to use $store.commit in Nuxt with #vue/composition-api - vue.js

<template>
<div>
<h1>Vuex Typescript Test</h1>
<button #click="handleLogin">click</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from '#vue/composition-api'
export default defineComponent({
setup() {
return {
handleLogin() {
// something....
},
}
},
})
</script>
#vue/composition-api do not apply useStore
I want to use store in setup function.

You should be able to access the useStore composable in the setup function according to the documentation of Vuex.
Your script section will look like this:
import { defineComponent } from '#vue/composition-api';
import { useStore } from 'vuex';
export default defineComponent({
setup() {
return {
const store = useStore();
return {
handleLogin {
store.dispatch('auth/login');
},
};
}
},
});
The proper way to structure the content of setup would be to move the handleLogin as a separate const and expose the constant in the return, in order to keep the return section more readable like this:
setup() {
const store = useStore();
const handleLogin = () => {
store.dispatch('auth/login');
};
return {
handleLogin,
}
}

Related

Vue warn]: Property "isMobileTerminal" was accessed during render but is not defined on instance

I'm working on a project that's both mobile and PC,I need to estimate the mobile terminal or PC terminal。
flexible.js
import { computed } from 'vue'
import { PC_DEVICE_WIDTH } from '../constants'
import { useWindowSize } from '#vueuse/core/index'
const { width } = useWindowSize()
// 判断当前是否为移动设备,判断依据屏幕宽度是否小于一个指定宽度(1280)
export const isMobileTerminal = computed(() => {
return width.value < PC_DEVICE_WIDTH
})
and the navigation/index.vue code is
<template>
<mobile-navigation v-if="isMobileTerminal"></mobile-navigation>
</template>
<script>
import { isMobileTerminal } from '../../../../utils/flexible'
import mobileNavigation from './mobile/index.vue'
export default {
name: 'index',
components: {
mobileNavigation
}
}
</script>
<style lang="scss" scoped></style>
My project catalog is shown below
isMobileTerminal is only imported in your component. It also needs to be made available to the template by declaring it in your component definition.
Returning it from the setup() hook is one way to do that:
<script>
import { isMobileTerminal } from '../../../../utils/flexible'
export default {
setup() {
return {
isMobileTerminal
}
}
}
</script>

I built a simple vuejs app with vuex but I would like to use mapGetters, how can I implement that function on it?

I built a simple vuejs app with vuex but I would like to use mapGetters, how can I implement that function on it?
this is my index.js:
import { mapGetters } from 'vuex'
import { createStore } from 'vuex'
import axios from 'axios'
export default createStore({
state: {
counter: 0,
colourCode: 'blue'
},
getters: {
counterSquared(state){
return state.counter * state.counter
}
},
and currently this how the vue component looks like:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<div
:style="{color: $store.state.colourCode}"
class="counter">
{{$store.state.counter}}
</div>
<div class="counter-squared">
{{$store.state.counter}}
<sup>2</sup> =
{{$store
.getters.counterSquared}}
</div>
How can I change it to using mapGetters?
Here's what you can do, in VUE 3
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
counterSquared: computed(() => store.getters.counterSquared)
}
}
}
</script>
Using setup template :
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const counterSquared: computed(() => store.getters.counterSquared)
}
}
</script>
Vue 2
<script>
import { mapGetters } from 'vuex'
export default {
// you can call it like this.counterSquad, counterSquad(in template)
computed: {
...mapGetters([
'counterSquared',
// ...
])
}
// OR
// you can call it like this.cS, cS(in template) base on defined name.
computed: {
...mapGetters({
cS: 'counterSquared'
})
])
}
}
</script>

[Vue warn]: Property or method "stateSidebar" is not defined on the instance but referenced during render

I know this seems like a question that would be easy to find, but my code worked some time ago. I am using a Vuex binding to check if my sidebar should be visible or not, so stateSidebar should be set within my entire project.
default.vue
<template>
<div>
<TopNav />
<SidebarAuth v-if="stateSidebar" />
<Nuxt />
</div>
</template>
<script>
import TopNav from './partials/TopNav';
import SidebarAuth from './partials/SidebarAuth';
export default {
components: {
TopNav,
SidebarAuth
},
methods: {
setStateSidebar(event, state) {
this.$store.dispatch('sidebar/setState', state)
}
}
}
</script>
store/sidebar.js
export const state = () => ({
stateSidebar: false
});
export const getters = {
stateSidebar(state) {
return state.stateSidebar;
}
};
export const mutations = {
SET_SIDEBAR_STATE(state, stateSidebar) {
state.stateSidebar = stateSidebar;
}
};
export const actions = {
setState({ commit }, stateSidebar) {
commit('SET_SIDEBAR_STATE', stateSidebar);
},
clearState({ commit }) {
commit('SET_SIDEBAR_STATE', false);
}
};
plugins/mixins/sidebar.js
import Vue from 'vue';
import { mapGetters } from 'vuex';
const Sidebar = {
install(Vue, options) {
Vue.mixin({
computed: {
...mapGetters({
stateSidebar: 'sidebar/stateSidebar'
})
}
})
}
}
Vue.use(Sidebar);
nuxt.config.js
plugins: ["./plugins/mixins/validation", "./plugins/axios", "./plugins/mixins/sidebar"],
If you're creating a mixin, it should be in /mixins
So for example /mixins/my-mixin.js.
export default {
// vuex mixin
}
Then import it like this in default.vue
<script>
import myMixin from '~/mixins/my-mixin`
export default {
mixins: [myMixin],
}
This is not what plugins should be used for tho. And IMO, you should definitively make something simpler and shorter here, with less boilerplate and that will not be deprecated in vue3 (mixins).
This is IMO the recommended way of using it
<template>
<div>
<TopNav />
<SidebarAuth v-if="stateSidebar" />
<Nuxt />
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState('sidebar', ['stateSidebar']) // no need to use object syntax nor a getter since you're just fetching the state here
},
}
</script>
No mixin, no plugin entry.

How to get set in composition api for step form?

I am trying to create a multi step form with composition api.
In vue 2 I used to do it this way
email: {
get() {
return this.$store.state.email
},
set(value) {
this.$store.commit("setEmail", value)
}
},
Now I have my own store, I made this computed property to pass to my component stEmail: computed(() => state.email). How can I actually use this in get set?
I am trying do something like this but completely doesn't work.
let setMail = computed(({
get() {
return stEmail;
},
set(val) {
stEmail.value = val;
}
}))
const state = reactive({
email: "",
})
export function useGlobal() {
return {
...toRefs(state),
number,
}
}
Or is there better way now to make multi step forms?
You can do the same with the Composition API. Import useStore from the vuex package and computed from vue:
import { computed } from 'vue';
import { useStore } from 'vuex';
And then use it in your setup() function like this:
setup: () => {
const store = useStore();
const email = computed({
get() {
return store.state.email;
},
set(value) {
store.commit("setEmail", value);
}
});
return { email };
}
If you want to avoid using vuex, you can just define variables with ref() and export them in a regular JavaScript file. This would make your state reusable in multiple files.
state.js
export const email = ref('initial#value');
Form1.vue/Form2.vue
<template>
<input v-model="email" />
</template>
<script>
import { email } from './state';
export default {
setup() {
return { email };
}
};
</script>
As Gregor pointed out, the accepted answer included an anonymous function that doesn't seem to work, but it will work if you just get rid of that part. Here's an example using <script setup> SFC
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const email = computed({
get() {
return store.state.email
},
set(value) {
store.commit("setEmail", value)
}
})
</script>
<template>
<input type="email" v-model="email" />
</template>

How to use Vuex mapGetters with Vue 3 SFC Script Setup syntax?

I'm refactoring component from regular Vue 3 Composition API to Script Setup syntax. Starting point:
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { mapGetters } from 'vuex';
export default defineComponent({
name: 'MyCoolBareComponent',
computed: {
...mapGetters('auth', ['isAdmin']),
},
});
</script>
Current Vue v3 migration documentation, SFC Composition API Syntax Sugar (< script setup >), links to this RFC page: https://github.com/vuejs/rfcs/pull/182
There is only one example for using computed reactive property:
export const computedMsg = computed(() => props.msg + '!!!')
As there is no current Vuex 4 documentation available that is mentioning <scrip setup>, it remains unclear to me how I should be using mapGetters when using this syntax? Or what is the correct way of going about this with Vuex 4?
tldr: scroll down to final result
There is now better documentation and the simple answer is: You don't need mapGetters but you can implement it yourself.
https://next.vuex.vuejs.org/guide/composition-api.html#accessing-state-and-getters
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const count = computed(() => store.getters.count)
</script>
If you have many getters you want to turn into a "computed property" you could use something as "intuitive" as this:
const { countIsOdd, countIsEven } = Object.fromEntries(Object.keys(store.getters).map(getter => [getter, computed(() => store.getters[getter])]))
Put that into a function and it even looks nice.
const mapGetters = (getters) => {
return Object.fromEntries(Object.keys(getters).map(getter => [getter, computed(() => getters[getter])]))
}
const { countIsOdd, countIsEven } = mapGetters(store.getters)
Put that function into a file and export it as a module...
// lib.js
import { computed } from 'vue'
import { useStore } from 'vuex'
const mapGetters = () => {
const store = useStore()
return Object.fromEntries(Object.keys(store.getters).map(getter => [getter, computed(() => store.getters[getter])]))
}
export { mapGetters }
...and you can easily use it in all your components.
// components/MyComponent.vue
<script setup>
import { mapGetters } from '../lib'
const { countIsOdd, countIsEven } = mapGetters()
</script>
Final result:
Here's the final lib.js I came up with:
import { computed } from 'vue'
import { useStore } from 'vuex'
const mapState = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store.state).map(
key => [key, computed(() => store.state[key])]
)
)
}
const mapGetters = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store.getters).map(
getter => [getter, computed(() => store.getters[getter])]
)
)
}
const mapMutations = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store._mutations).map(
mutation => [mutation, value => store.commit(mutation, value)]
)
)
}
const mapActions = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store._actions).map(
action => [action, value => store.dispatch(action, value)]
)
)
}
export { mapState, mapGetters, mapMutations, mapActions }
Using this in the component looks like this:
<template>
Count: {{ count }}
Odd: {{ counterIsOdd }}
Even: {{ counterIsEven }}
<button #click="countUp">count up</button>
<button #click="countDown">count down</button>
<button #click="getRemoteCount('https://api.countapi.xyz')">
get remote count
</button>
</template>
<script setup>
import { mapState, mapGetters, mapMutations, mapActions } from '../lib'
// computed properties
const { count } = mapState()
const { countIsOdd, countIsEvent } = mapGetters()
// commit/dispatch functions
const { countUp, countDown } = mapMutations()
const { getRemoteCount } = mapActions()
</script>
Any feedback on this would be very appreciated.
So far this syntax seems to be working. However, I'm hoping that Vuex would develop a cleaner way for exposing computed getters for template.
If you know a better way, we'd love to hear!
<script setup lang="ts">
import { mapGetters } from 'vuex';
export const name = 'MyCoolBareComponent';
export default {
computed: {
...mapGetters('user', ['profile', 'roles']),
},
};
</script>
import {useStore} from "vuex";
import {computed} from "vue";
const {getEvents, getSelectedTag} = useStore().getters;
const events = computed(() => getEvents)
const selectedTag = computed(() => getSelectedTag)
i do this and for me is working
You don't need to export anything, an SFC will register all variables and components for you and make them available in template.
An SFC automatically infers the component's name from its filename.
Here are a few examples that may be useful:
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import MyComponent from './components/MyComponent'
const store = useStore()
const data = 'Random string as a data'
// without module/data
const myAction = () => store.dispatch('myAction')
// with data
const mySecondAction = () => store.dispatch('mySecondAction', data)
// with module
const myMutation = () => store.commit('moduleName/myMutation')
// with module/data
const myNewMutation = () => store.commit('moduleName/myNewMutation', data)
const myStateVariable = computed(() => store.state.myStateVariable)
// with module
const myGetter = computed(() => store.getters.moduleName.myGetter)
// replace using of mapState/mapGetters
const state = computed(() => store.state)
// and then
console.log(state.myStateVariable)
console.log(state.mySecondStateVariable)
....
</script>
You can do something like this
import { mapGetters } from "vuex"
setup() {
return {
...mapGetters("myModule", ["doSomething"])
}
}
Follow this:
https://vuex.vuejs.org/guide/typescript-support.html#typing-usestore-composition-function
Here is an example:
store.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'
// define your typings for the store state
export interface State {
token: string|null
}
// define injection key
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({
state: {
token: localStorage.getItem('token') ? localStorage.getItem('token'):'',
}
})
main.js
import { store, key } from './store'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// pass the injection key
app
.use(store, key)
.mount('#app')
In a vue component
<script setup>
import { onMounted } from 'vue'
import { useStore } from 'vuex'
import { key } from './store'
const token = useStore(key)
onMounted(() => {
console.log(store.state.token)
})
</script>