Unable to access vuex-orm $db variable from store in nuxt - vue.js

Using nuxtjs with vuexorm & vuexorm-axios plugin.
/pages/index.vue
computed: {
users() {
// this works as expected
return User.all()
}
}
plugins/vuex-orm-axios.js
import { Model } from '#vuex-orm/core'
export default ({ $axios }) => {
Model.setAxios($axios)
}
store/index.js
import VuexORM from '#vuex-orm/core'
import VuexORMAxios from '#vuex-orm/plugin-axios'
import User from '#/models/user'
VuexORM.use(VuexORMAxios)
// Create a new database instance.
const database = new VuexORM.Database()
// Register Models to the database.
database.register(User)
export const plugins = [
VuexORM.install(database)
]
Above all works. But in vuexorm docs it says to always fetch model from injected database instance for nuxt/ssr apps.
But if I try to access the $db variable from store it wont work as there is no $db variable inside store.
/pages/index.vue
computed: {
users() {
// this wont work as $db is undefined
User () {
return this.$store.$db().model('users')
},
users () {
return this.User.all()
}
}
What am I doing wrong here?

In your store/index.js you have to export state to activate Vuex store. Add this to the file:
export const state = () => ({})

Related

How can I bind a ref to a Pinia state variable through an action, in Vue 3?

I'm using the useFirestore composable from the vueUse library - I had success in reactively binding my "titles" document to the titles store variable, however when I try to bind userData through an action nothing happens (note: my Firebase config is fine).
What's the correct way to do this?
// user.store.js
import { defineStore } from "pinia";
import { useFirestore } from "#vueuse/firebase/useFirestore";
import { db, doc } from "../../../config/firebase";
export const useUserStore = defineStore("user", {
state: () => ({
titles: useFirestore(doc(db, "titles", "available")), // <-- this works and binds the firestore document
userData: null,
}),
getters: {
getUserData: (state) => state.userData,
},
actions: {
setUserData(uid) {
this.userData = useFirestore(doc(db, "users", uid)); // <-- this doesn't do anything and userData is `null` in the dev tools.
},
}
});
// Component.vue
...
setUserData("my-id");
Ah, I neglected to use $patch. This worked for me:
setUserData(uid) {
const user = useFirestore(doc(db, "users", uid));
this.$patch({ userData: user });
}
If I'm using this in the wrong way, please let me know.

How to pass an argument to Pinia store?

I'm making a session API call in main.js and using values from the response as the initial value for my root store. In vuex it's handled this like,
DataService.getSession()
.then((sessionData) => {
new Vue({
i18n,
router,
// this params sessionData.session will be passed to my root store
store: store(sessionData.session),
render: (h) => h(App),
}).$mount('#app');
})
Consumed like,
export default function store(sessionData) { // here I'm getting the sessionData
return new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
state: {
// some states here
},
});
}
In case of Pinia we're creating a app instance & making it use like,
app.use(createPinia())
And my store would be like,
// how to get that sessionData here
import { defineStore } from 'pinia'
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({
counter: 0
})
})
Is it possible to pass the sessionData someway to the pinia store?
There are 3 ways to pass parameters to a Pinia store - see the list below. You could use either #2 or #3 .
In most cases it is wise to initialise your Vue instance and show the user something while they are waiting for connections to resolve. So you may find it simpler to just access or initialise the store by calling DataService.getSession() in say a "SessionStore" action which can be async. Typically Components =access=> Stores =access=> Services.
Unlike Vuex, you don't need a root Pinia store. You can get just call useSomeStore() in the setup method for any component. Each store can just be an island of data. Pinia stores can reference other pinia store instances. This might be particularly useful if you're migrating a set of Vuex stores to Pinia and need to preserve the old Vuex tree of stores.
1. Pass common params to every action.
export const useStore = defineStore('store1', {
state: () => ({
...
}),
actions: {
action1(param1: string ... ) {
// use param1
}
}
});
2. Initialise store AFTER creating it
Only works if there's one instance of this store required
export const useStepStore = defineStore('store2', {
state: () => ({
param1: undefined | String,
param2: undefined | String,
...
}),
getters: {
getStuff() { return this.param1 + this.param2; }
}
actions: {
init(param1: string, param2: string) {
this.param1 = param1
this.param2 = param2
},
doStuff() {
// use this.param1
}
}
});
3. Use the factory pattern to dynamically create store instances
// export factory function
export function createSomeStore(storeId: string, param1: string ...) {
return defineStore(storeId, () => {
// Use param1 anywhere
})()
}
// Export store instances that can be shared between components ...
export const useAlphaStore = createSomeStore('alpha', 'value1');
export const useBetaStore = createSomeStore('beta', 'value2');
You could cache the session data in your store, and initialize the store's data with that:
In your store, export a function that receives the session data as an argument and returns createPinia() (a Vue plugin). Cache the session data in a module variable to be used later when defining the store.
Define a store that initializes its state to the session data cached above.
In main.js, pass the session data to the function created in step 1, and install the plugin with app.use().
// store.js
import { createPinia, defineStore } from 'pinia'
1️⃣
let initData = null
export const createStore = initStoreData => {
initData = { ...initStoreData }
return createPinia()
}
export const useUserStore = defineStore('users', {
state: () => initData, 2️⃣
})
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { createStore } from './store'
import * as DataService from './data-service'
DataService.getSession().then(sessionData => {
createApp(App)
.use(createStore(sessionData)) 3️⃣
.mount('#app')
})
demo
When you create a store in Pinia using defineStore() you give it the initial state. So wherever you do that just pass the data into it and then do
defineStore('name', {
state: () => {
isAdmin: session.isAdmin,
someConstant: 17
},
actions: { ... }
});

Nuxt access store (in Module mode) from JS file

I have an AuthService that I use in a namespaced store in my Nuxt app. I need to commit mutations from AuthService to the namespaced store but I can't figure out how to import the store into my AuthService.
I've seen examples where the store is imported into the JS file, but the store is explicitly defined in the Vue app. Because I'm using Nuxt with the Module mode for my store, I'm not sure of the root path where I can import my store into the AuthService file. As I understand it, Nuxt handles creating the root store and all the namespaced store behind the scenes when use "Module mode"
My Nuxt store directory includes index.js (which is empty) and auth.js which has the mutations I want to call from AuthService.
auth.js
import AuthService from '../firebase/authService'
const authService = new AuthService()
export const state = () => ({
user: null
})
export const mutations = {
setUser (state, user) {
state.user = user
}
}
export const actions = {
async signUp ({ commit }, payload) {
try {
await authServices.createUser(payload)
return Promise.resolve()
} catch (err) {
const notification = {
duration: 5000,
message: err.message,
type: 'error'
}
commit('ui/activateNotification', notification, { root: true })
return Promise.reject()
}
}
}
authService.js
import { fAuth, fDb } from './config'
// I think I need to import auth store here but I'm not sure how
export default class AuthClient {
async createUser (payload) {
try {
const res = await fAuth.createUserWithEmailAndPassword(payload.email, payload.password)
const { uid } = res.user
const user = {
...payload,
uid
}
await this._createUserDoc(user)
this._initAuthListener()
return Promise.resolve()
} catch (err) {
return Promise.reject(err)
}
}
async _createUserDoc (user) {
await fDb.collection('users').doc(user.uid).set(user)
}
_initAuthListener () {
fAuth.onAuthStateChanged(async (user) => {
try {
if (user) {
const userProfileRef = fDb.collection('users').doc(user.uid)
const userProfileDoc = await userProfileRef.get()
const { uid, userName } = userProfileDoc.data()
// Here is where I want to call a mutation from the auth store
this.store.commit('setUser', {
uid,
userName
})
} else {
this.store.commit('setUser', null)
}
} catch (err) {
console.log(err)
}
})
}
}
I'd like to propose a solution using a plugin.
In the external module (externalModule.js) we define store variable and export an init function that receives Nuxt context as argument. The function assignes the store from context to the variable which can be now used in the module:
let store;
export function init (context) {
store = context.store;
};
(...further business logic using store)
Then in the plugins folder we create a plugin file (let's call it storeInit.js). The file imports the init function from the external module and exports default plugin function required by Nuxt. The function receives context from Nuxt and we call the init function passing the context further:
import { init } from '[pathTo]/externalModule.js';
export default (context, inject) => {
init(context);
};
Then we register the plugin in the nuxt.config.js file:
module.exports = {
...
plugins: [
{ src: '~/plugins/storeInit' }
],
...
}
This way when the app is built by Nuxt and plugins are registered, the context object is passed to the external module and we can use anything from it, among others the store.
In index.js file which is in store folder you need to return store like this
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
state: {
counter: 0
},
mutations: {
increment (state) {
state.counter++
}
}
})
}
export default createStore
and in your authService.js file you need to import store like this
import $store from '~/store'
by this you will be able to access your store
$store.commit('setUser', null)
I hope this works for you
Important Note: you don't need to install vuex because it is already shipped with nuxtjs
You can access as window.$nuxt.$store
Note: My nuxt version is 2.14.11

Not able to access i18 plugin from mutation in classic mode store in Nuxt application

I'm trying to implement Vuex i18n package within my Nuxt application. In my nuxt.conf.js file in plugins array I have:
{
src: '#/plugins/i18n.js',
ssr: false
},
plugins/i18n.js file is:
import Vue from "vue";
import vuexI18n from "vuex-i18n/dist/vuex-i18n.umd.js";
import toEnglish from "../translations/toEnglish";
import toSpanish from "./../translations/toSpanish";
import toGerman from "./../translations/toGerman";
export default ({ store }) => {
Vue.use(
vuexI18n.plugin,
store,
{
onTranslationNotFound: function (locale, key) {
console.warn(`vuex-i18n :: Key '${key}' not found for locale '${locale}'`)
}
}
);
// register the locales
Vue.i18n.add('en', toEnglish);
Vue.i18n.add('de', toGerman);
Vue.i18n.add('es', toSpanish);
// Set the start locale to use
Vue.i18n.set('de');
Vue.i18n.fallback('en');
}
Last thing is my store. I'm using classic mode of vuex store in Nuxt:
import Vuex from "vuex";
const store = () => {
return new Vuex.Store({
state: () => ({
currentLanguage: ''
}),
mutations: {
changeLang(state, response) {
if (response) {
console.log(this);
state.currentLanguage = response;
this.i18n.set(response);
}
}
}
})
};
export default store;
As you can see in store file in mutation I'm trying to access i18n plugin with this keyword. Unfortunetally in print error in console:
TypeError: Cannot read property 'set' of undefined
this which I consoled also inside mutation is:
I changed this.i18n.set(response); to state.i18n.locale = response; inside my mutation and now it seems working.
For some reason when I call this mutation my video.js player refresh. I will try to find out why.

access store from outside js file in nuxtjs

I'm trying to access the store from a file outside a component
When I search for this problem I saw that people say that I should import the store from my file and then I can get access to it, but I can't make this work
my store is built like this:
const createStore = () => {
return new Vuex.Store({
state: { ... },
getters: { ... },
mutations: { ... },
actions: { ... },
})
}
and I've tried to import it in my js file like I saw recommended
import store from '#/store'
Any ideas?
You could import the store by registering it upon instantiation, like this:
External file some-service.js:
export default class SomeService {
constructor(store) {
this.store = store
}
doStuff() {
this.store.dispatch('someAction')
}
}