Using store in component with Quasar - vue.js

I'm trying to implement Vuex store in a Quasar project. I created a new project using the quasar-cli and checked the Vuex box. I then followed the guide on the quasar website (https://quasar.dev/quasar-cli/cli-documentation/vuex-store)
and created a new store using quasar new store test
I then registered the store module in the store/index.js
export default function(/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
test
// example
},
Afterwards, I added the mutation and state code, exactly as referenced in the tutorial.
Then I created a new component (test) and added the code as explained.
However, I am unable to use this.$store, and receive a warning from my IDE that $store is not defined.
I have read the Vuex documentation, which writes that it is possible to pass the state to all components by adding the state to the object in main.js. As far as i can see, quasar does this already.
So, what am I doing wrong and how can you use store without manually importing it for every single component?

import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
// general state
app
},
mutations: {
someMutation (state, store) {
}
},
actions: {
someAction ({commit}) {
},
})
export default store
Also don't forget to include this store in app.js

Vue.use(Vuex) was missing
Vuex provides a mechanism to "inject" the store into all child components from the root component with the store option (enabled by Vue.use(Vuex))

It took me a while to get it working but here is an example of my state
user :
{
uid: '',
name: '',
accountType: ''
}
}
const mutations = {
setName (state, val) {
state.user.name = val
},
setUID (state, val) {
state.user.uid = val
},
setAccountType (state, val) {
state.user.accountType = val
}
}
const actions = {
}
const getters = {
user: (state) => {
return state.user
}
}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}
then in each file if you want to access this information you have to use
computed : {
user () {
return this.$store.getters['user/user']
}
}
I wanted to display this information in my tags and can do so with
<template>
<div class="user-profile">
{{ user.name }}
{{ user.email }}
{{ user.accountType }}
</div>
</template>
hope that helps.
note rather than a folder with my modules I have it all in one file 'store-user.js' and in my store/index.js I have
import user from './store-user'
and
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
modules: {
user
}
// enable strict mode (adds overhead!)
// for dev mode only
strict: process.env.DEV
})
return Store
}

Related

Modules vs Multiple vuex store files

I am working on a project using vue and vuex. And I think most of the people are having the problem, after sometime the store.js (or index.js) is getting too big. So I want to split the store.js file. After some google I found I can use Modules to overcome this problem. BUT I tried also with creating a new Instance of vuex and it works perfectly fine.
Single instance with modules :
---store.js
import Vuex from "vuex";
import thisismodule1 from "./modules/module1";
import thisismodule2 from "./modules/module2";
const createStore = () => {
return new Vuex.Store({
modules: {
module1: thisismodule1,
module2: thisismodule2
}
});
};
export default createStore;
const store = new Vuex.Store({
module
});
Multiple files with multiple instances:
---storeCar.js
---storeHouse.js
---storeTree.js
...
So my question is, is this allowed or do I have to use modules with single instance?
Thank you in advance!
there is a best practice for that:
Create a file. that's name is Shared
Create a Store folder and create a modules folder on it:
you should modules in the modules folder and define your store for a target:
for example:
import * as types from "../types";
const state = {
currentPage: {}
};
const getters = {
[types.avatarManagement.getters.AVATAR_MANAGEMENT_GET]: state => {
return state.currentPage;
}
};
const mutations = {
[types.avatarManagement.mutations.AVATAR_MANAGEMENT_MUTATE]: (
state,
payload
) => {
state.currentPage = payload;
}
};
const actions = {
[types.avatarManagement.actions.AVATAR_MANAGEMENT_ACTION]: (
{ commit },
payload
) => {
commit(types.avatarManagement.mutations.AVATAR_MANAGEMENT_MUTATE, payload);
}
};
export default {
state,
getters,
mutations,
actions
};
Create index.js for define Vuex and import your modules:
index.js file:
import Vue from "vue";
import Vuex from "vuex";
import avatarManagement from "./modules/avatarManagement";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
avatarManagement
}
});
5) also you can types of your vuex store on Type.js file:
type.js:
export const avatarManagement = {
getters: {
AVATAR_MANAGEMENT_GET: "AVATAR_MANAGEMENT_GET"
},
mutations: {
AVATAR_MANAGEMENT_MUTATE: "AVATAR_MANAGEMENT_MUTATE"
},
actions: {
AVATAR_MANAGEMENT_ACTION: "AVATAR_MANAGEMENT_ACTION"
}
};
***for get data from Store:
computed: {
...mapGetters({
registrationData:types.avatarManagement.AVATAR_MANAGEMENT_GET,
getDataFromStore() {
return this.registrationData;
}
}
***for Change data to Store and mutate that:
methods: {
goToActivity() {
const activity = {
companyList: this.categories
};
this.$store.commit(types.avatarManagement.AVATAR_MANAGEMENT_MUTATE, {
newData
});
},
}

Nuxt acces vuex store inside mixin

I m making small mixin for driving animations based on how user is crawling through page and I need to have access to data stored in Vuex store from that mixin.
Any ides how to pass data from vuex store there, I am getting mad.
pages/index.vue
import Vue from 'vue'
import {ToplevelAnimations} from '~/mixins/toplevel_animations'
export default Vue.extend({
mixins: [
ToplevelAnimations
]
})
mixins/toplevel_animations.js
export const ToplevelAnimations = {
transition(to, from) {
// some logic, here I need to access routes stored in store
return (to_index < from_index ? 'switch-to-left' : 'switch-to-right');
}
}
store/index.js
import {StoreExtensions} from "~/plugins/store_extensions.js";
export const state = () => ({
MAIN_NAV_LINKS: [
{
path: '/',
title: 'Domov',
order: 1
},
// ...etc
],
CURRENT_PAGE_INDEX: 1
})
export const getters = {
getMainNavLinks: (state, getters) => {
return state.MAIN_NAV_LINKS
}
// ..etc
}
export const mutations = {
setCurrentPageIndex(index) {
state.CURRENT_PAGE_INDEX = index
}
// ...etc
}
export const actions = {
nuxtServerInit ({ commit }, req ) {
var page_index = StoreExtensions.calculatePageIndex(req.route.path, state.MAIN_NAV_LINKS)
mutations.setCurrentPageIndex(page_index)
}
}
I never used Vue.extend(), always using .vue files, so it might be different (although it should not). From my experience with mixins in both Vue and Nuxt seems that mixins receive the relevant this context when they are assigned to components. So for me using simple this.$store worked.

vuex unknown action type when registering module from plugin

I have a plugin with its own store. The store seems to register OK when the plugin is installed and I can see the state under the given namespace in vue dev tools, but the component in the plugin has no access to the state and when I try to call an action it is not found.
I'm pretty sure it is a namespacing issue, but I can't figure out what to add where in the install function.
In the plugin, the store looks like this:
Vue.use(Vuex)
export default new Vuex.Store({
namespaced: true,
state: { counter: 0 },
getters: {},
mutations: { increment: (state) => { state.counter += 1 } },
actions: { plusone: ({ commit }) => {
alert('Inside plusone Action: ')
commit('increment');
},
}
})
And my plugin install file looks like this:
import component from './SimpleCounter.vue'
import store from './store'
function install(Vue, options) {
if (install.installed) return
install.installed = true
if (!options || !options.store) { throw new Error('Please initialise plugin with a Vuex store.') }
options.store.registerModule('simpleCounter', store)
Vue.component('SimpleCounter', component)
}
const plugin = { install }
...
The method in the component is a simple dispatch:
increment () {
this.$store.dispatch('plusone')
}
In the parent app that is consuming the plugin, this is how it is added:
import SimpleCounter from 'mcflyal'
Vue.use(SimpleCounter, { store })
Vue.config.productionTip = false
new Vue({ router, store,
render: h => h(App)
}).$mount('#app')
The vuex docs there is mention of "receive a namespace value via your plugin option", but I don't know if that is the right way and how.

Vuex-persistedstate does not work after refreshing a page

I am making a spa app with laravel and vue, and I would like to keep my login information in vuex's state after refreshing a page.
I typed
sudo npm install vuex-persistedstate
and installed Vuex-persistedstate and set the plugins as below.
import Vue from 'vue' import Vuex from 'vuex'
import auth from './auth'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
auth,
plugins: [createPersistedState()],
}
})
export default store
I expected my data to be kept after refreshing a page but it was not.
I checked what is going on, and it seems the plugin object was empty.
it seems that I cannot import the plugins for some reason, but I do not know why.
Could someone help me, please?
my module file
const state = {
user: null,
salonId: null
}
const getters = {
check: state => !! state.user,
checkSalonId: state => {return state.salonId},
}
const mutations = {
setUser (state, user) {
state.user = user
},
setSalonId (state, salonId) {
state.salonId = salonId
}
}
const actions = {
async currentUser (context) {
const response = await axios.get('/api/user')
const user = response.data || null
context.commit('setUser', user)
},
async logout (context) {
context.commit('setUser', null)
context.commit('setSalonId', null)
},
async currentSalon (context, salonId) {
context.commit('setSalonId', salonId)
}
}
export default {
namespaced: true,
state,
getters,
mutations,
actions,
}
You placed plugins in the wrong place. plugins should be inline with modules, not inside modules.
Please take a look at this differences.
const store = new Vuex.Store({
modules: {
auth,
plugins: [createPersistedState()]
}
})
VS
const store = new Vuex.Store({
modules: {
auth
},
plugins: [createPersistedState()]
})
I think you need to setup an option object with the modules you wanna persist. In this example i'm persisting the user module from my store (haven't include the import code)
const persistedStateOptions = {
paths: [
'user',
]
}
export default new Vuex.Store({
modules: {
user,
},
plugins: [createPersistedState(persistedStateOptions)]
})
Look at difference in paths.This worked for me.
import createPersistedState from 'vuex-persistedstate';
Vue.use(Vuex);
export default new Vuex.Store({
strict:true,
modules:{
party,
contract
},
plugins: [createPersistedState(
{
paths:['party.selectedParty']
}
)],
});
Where party is the module name and selectedParty is the name of module state.
Now this plugin only taking care of selectedParty state.
The vue components that get data via state from DB should be kept out of this plugin.

Making Async Calls With Vuex

I'm just starting to learn Vuex here. Until now I've been storing shared data in a store.js file and importing store in every module but this is getting annoying and I'm worried about mutating state.
What I'm struggling with is how to import data from firebase using Vuex. From what I understand only actions can make async calls but only mutations can update the state?
Right now I'm making calls to firebase from my mutations object and it seems to be working fine. Honestly, all the context, commit, dispatch, etc. seems a bit overload. I'd like to just be able to use the minimal amount of Vuex necessary to be productive.
In the docs it looks like I can write some code that updates the state in the mutations object like below, import it into my component in the computed property and then just trigger a state update using store.commit('increment'). This seems like the minimum amount necessary to use Vuex but then where do actions come in? Confused :( Any help on the best way to do this or best practices would be appreciated!
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
My code is below
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const db = firebase.database();
const auth = firebase.auth();
const store = new Vuex.Store({
state: {
userInfo: {},
users: {},
resources: [],
postKey: ''
},
mutations: {
// Get data from a firebase path & put in state object
getResources: function (state) {
var resourcesRef = db.ref('resources');
resourcesRef.on('value', snapshot => {
state.resources.push(snapshot.val());
})
},
getUsers: function (state) {
var usersRef = db.ref('users');
usersRef.on('value', snapshot => {
state.users = snapshot.val();
})
},
toggleSignIn: function (state) {
if (!auth.currentUser) {
console.log("Signing in...");
var provider = new firebase.auth.GoogleAuthProvider();
auth.signInWithPopup(provider).then( result => {
// This gives you a Google Access Token. You can use it to access the Google API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// Set a user
var uid = user.uid;
db.ref('users/' + user.uid).set({
name: user.displayName,
email: user.email,
profilePicture : user.photoURL,
});
state.userInfo = user;
// ...
}).catch( error => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});
} else {
console.log('Signing out...');
auth.signOut();
}
}
}
})
export default store
main.js
import Vue from 'vue'
import App from './App'
import store from './store'
new Vue({
el: '#app',
store, // Inject store into all child components
template: '<App/>',
components: { App }
})
App.vue
<template>
<div id="app">
<button v-on:click="toggleSignIn">Click me</button>
</div>
</template>
<script>
import Hello from './components/Hello'
export default {
name: 'app',
components: {
Hello
},
created: function () {
this.$store.commit('getResources'); // Trigger state change
this.$store.commit('getUsers'); // Trigger state change
},
computed: {
state () {
return this.$store.state // Get Vuex state into my component
}
},
methods: {
toggleSignIn () {
this.$store.commit('toggleSignIn'); // Trigger state change
}
}
}
</script>
<style>
</style>
All AJAX should be going into actions instead of mutations. So the process would start by calling your action
...which commits data from the ajax callback to a mutation
...which is responsible for updating the vuex state.
Reference: http://vuex.vuejs.org/en/actions.html
Here is an example:
// vuex store
state: {
savedData: null
},
mutations: {
updateSavedData (state, data) {
state.savedData = data
}
},
actions: {
fetchData ({ commit }) {
this.$http({
url: 'some-endpoint',
method: 'GET'
}).then(function (response) {
commit('updateSavedData', response.data)
}, function () {
console.log('error')
})
}
}
Then to call your ajax, you will have to call the action now by doing this:
store.dispatch('fetchData')
In your case, just replace this.$http({...}).then(...) with your firebase ajax and call your action in the callback.