Accessing vue mixin on vuex action - vue.js

I created vue mixin function that return the base url of the project. The reason I needed this mixin is because on the dev stage the value will come from configuration file, and on production stage it'll come from global variable window.
The backEndHost is accessible from any .vue file, but it's not accessible from vuex action.
Vue mixin declaration:
Vue.mixin({
data: () => ({
get backEndHost() {
// ...
return (isDev) devAddr : prodAddr
}
})
})
Vuex actions declaration:
const actions = {
getChallengesData({ commit, state }, task) {
// ...
axios.get(this.backEndHost() + url).catch((thrown) => {
swal('Error!', thrown.message, 'error')
}).then((res) => {
// ...
})
}
}
The axios.get(this.backEndHost() + url) triggered an error, saying that this.backEndHost is undefined.
[Vue warn]: Error in mounted hook: "TypeError: this.backEndHost is not a function"
Any idea how to resolve this error? or is there any workaround to achieve the same result?

I ended up putting the backEndHost into utility helper (thanks to #ittus). Since I still need the backEndHost to be available on all components, I also put it into mixin.
File: helper.js
export function backEndHost() {
return (isDev) devAddr : prodAddr
}
File: main.js
import { backEndHost } from '#/utility/helper'
Vue.mixin({
data: () => ({
backEndHost
})
})
File: actions.js
import { backEndHost } from '#/utility/helper'
const actions = {
getChallengesData({ commit, state }, task) {
// ...
axios.get(backEndHost() + url).catch((thrown) => {
swal('Error!', thrown.message, 'error')
}).then((res) => {
// ...
})
}
}

Related

Using XState in Nuxt 3 with asynchronous functions

I am using XState as a state manager for a website I build in Nuxt 3.
Upon loading some states I am using some asynchronous functions outside of the state manager. This looks something like this:
import { createMachine, assign } from "xstate"
// async function
async function fetchData() {
const result = await otherThings()
return result
}
export const myMachine = createMachine({
id : 'machine',
initial: 'loading',
states: {
loading: {
invoke: {
src: async () =>
{
const result = await fetchData()
return new Promise((resolve, reject) => {
if(account != undefined){
resolve('account connected')
}else {
reject('no account connected')
}
})
},
onDone: [ target: 'otherState' ],
onError: [ target: 'loading' ]
}
}
// more stuff ...
}
})
I want to use this state machine over multiple components in Nuxt 3. So I declared it in the index page and then passed the state to the other components to work with it. Like this:
<template>
<OtherStuff :state="state" :send="send"/>
</template>
<script>
import { myMachine } from './states'
import { useMachine } from "#xstate/vue"
export default {
setup(){
const { state, send } = useMachine(myMachine)
return {state, send}
}
}
</script>
And this worked fine in the beginning. But now that I have added asynchronous functions I ran into the following problem. The states in the different components get out of sync. While they are progressing as intended in the index page (going from 'loading' to 'otherState') they just get stuck in 'loading' in the other component. And not in a loop, they simply do not progress.
How can I make sure that the states are synced in all my components?

How to Implement nuxtServerInit Action to load data from server-side on the initial load in Pinia (Nuxt3)

My Code:
export const useMenuStore = defineStore("menuStore", {
state: () => ({
menus: [],
}),
actions: {
async nuxtServerInit() {
const { body } = await fetch("https://jsonplaceholder.typicode.com/posts/1").then((response) => response.json());
console.log(body);
this.menus = body;
resolve();
},
},
});
NuxtServerInit is not working on initial page render on nuxt js vuex module mode.Anyone know this error please help me.
NuxtServerInit is not implemented in Pinia, but exists a workaround.
Using Pinia alongside Vuex
// nuxt.config.js
export default {
buildModules: [
'#nuxtjs/composition-api/module',
['#pinia/nuxt', { disableVuex: false }],
],
// ... other options
}
then Include an index.js file inside /stores with a nuxtServerInit action which will be called from the server-side on the initial load.
// store/index.js
import { useSessionStore } from '~/stores/session'
export const actions = {
async nuxtServerInit ({ dispatch }, { req, redirect, $pinia }) {
if (!req.url.includes('/auth/')) {
const store = useSessionStore($pinia)
try {
await store.me() // load user information from the server-side before rendering on client-side
} catch (e) {
redirect('/auth/login') // redirects to login if user is not logged in
}
}
}
}
In Nuxt2, the Nuxt will run the code in nuxtServerInit() of store/index.js on the server-side to boot the app.
However, in Nuxt3, there is no specific place to write the boot code, you can write the boot code anywhere instead of in nuxtServerInit() of store/index.js.
It might be helpful, especially when you need to send a request before boosting the app.
your pinia file may define like following:
store/menu.js
import { defineStore } from 'pinia';
export const useMenuStore = defineStore('menuStore', {
state: () => ({
_menus: [],
}),
getters: {
menus() {
return this._menus;
}
},
actions: {
async boot() {
const { data } = await useFetch('https://jsonplaceholder.typicode.com/posts/1');
this._menus = data;
}
}
});
Then, create a plugin which named as *.server.[ts|js], for example init.server.js
(.sever.js tail will let the file only run in server side)
plugins/init.server.js
import { defineNuxtPlugin } from '#app';
import { useMenuStore } from '~/store/menu.js';
export default defineNuxtPlugin(async (nuxtApp) => {
const menu = useMenuStore(nuxtApp.$pinia);
await menu.boot();
});
nuxt.config.js
modules: [
'#pinia/nuxt',
],
There is an entire example of SSR Nuxt3 with authorization that may help

In Nuxt, how to mutate a Vuex state in one module from the other?

I tried many things mentioned on the portal but nothing seems to work for me so posting here for some work-around.
I have 2 modules within my Nuxtjs application folder store\modules: ModuleA and ModuleB. For some verification in ModuleB I would like to access the state from ModuleA but for some reason, it's failing.
I tried rootScope, import etc but it did not work for me.
My state/modules/ModuleA.js:
export const state = () => ({
eventType: 'MyEvent',
})
export const mutations = {
eventTypePopulator (state, event) {
state.eventType = event
},
}
My state/modules/ModuleB.js:
export const state = () => ({
input: {}
})
export const mutations = {
jsonPreparation ({state, rootState}, payload) {
console.log(rootState.eventType)
// console.log($store.state.ModuleA.eventType)
}
}
I tried to import ModuleA into ModuleB and use it but that also did not work for me. Can someone please help me how can I access the state from one Module in another Module within Nuxtjs/Vuejs
As shown in the API reference, rootState is available in actions and getters.
It did not found any way of using it directly into a mutation.
Meanwhile, this can be achieved by passing it as a param to the mutation like this
ModuleB.js
const mutations = {
NICE_TASTY_MUTATION: (_state, { rootState, payload }) => {
// _state is not used here because it's moduleB's local state
rootState['moduleA'].eventType = payload
},
}
const actions = {
async myCoolAction({ commit, rootState }, { ...}) {
commit('NICE_TASTY_MUTATION', {
rootState,
payload: 'some-stuff'
})
}
}
And this could be called in a .vue file with something like this
methods: {
...mapActions('ModuleB', ['myCoolAction']),
}
...
await this.myCoolAction()
PS: IMO the convention is more to name the file module_b.js.

Check if vuex-persist has restored data in Nuxt project

I have added Vuex-Persist and Vuex-ORM to my Nuxt project. When the application starts for the first time I want to add some boilerplate data.
In my default.vue layout I have added a created function to add this dummy data.
<script>
import Project from '../models/Project';
export default {
created () {
// I'm Using Vuex-Orm to check if there are any projects stored
if (Project.query().count() === 0) {
Project.insert({ data: [{ title: 'My first project' }] })
}
}
}
</script>
When the application is reloaded and opens for the second time, I would expect that Project.query().count()
returns 1. But it will always return 0 because vuex-persist isn't done restoring the local data yet.
According to the docs this would be the solution
import { store } from '#/store' // ...or wherever your `vuex` store is defined
const waitForStorageToBeReady = async (to, from, next) => {
await store.restored
next()
}
store.defined is undefined and same goes for this.$store.
That comment highlights my exact question "Where is my vuex store defined?"
i think you have to put the whole thing in a route guard.
create a route-guard.js plugin like this. but I haven't tested the whole thing, hope it helps you further.
export default function ({app}) {
const waitForStorageToBeReady = async (to, from, next) => {
await store.restored
next()
}
app.router.beforeEach(waitForStorageToBeReady);
}
Another option is to put a getter in computed and watch it:
export default {
computed: {
persistState() {
return this.store.getter['get_persis_state'];
}
},
watch: {
persistState(newVal) {
// check new val
}
}
}
I followed the instructions from vuex-persist for Nuxt and made a plugin file, like this:
// ~/plugins/vuex-persist.js
import VuexPersistence from 'vuex-persist'
export default ({ store }) => {
window.onNuxtReady(() => {
new VuexPersistence({
/* your options */
}).plugin(store);
});
}
window.onNuxtReady caused the plugin to be loaded after all other code had run. So I didn't matter if I made a router-guard.js plugin or tried it in the layout/default.vue file.
I ended up with the quick fix:
// ~/plugins/vuex-persist.js
import VuexPersistence from 'vuex-persist'
export default ({ store }) => {
window.onNuxtReady(() => {
new VuexPersistence().plugin(store);
if (Project.query().count() === 0) {
Project.insert({ data: [{ title: 'My first project' }] })
}
});
}

Jest + Nuxt + Nuxt-Fire is failing in test suite

I'm using Nuxt with Nuxt-Fire (https://github.com/lupas/nuxt-fire)
When I launch my test I get this error [Vue warn]: Error in config.errorHandler: "TypeError: Cannot read property 'ref' of undefined"
This is happening because of this section in my App
mounted() {
this.initiate(window.instaroomId)
let connected = this.$fireDb.ref(".info/connected")
this.getConnection(connected)
},
It looks like the this.$fireDb is not called. The module is normally loaded in nuxt.config.js. How can I make this work?
If you want to test, that this.$fireDb.ref(".info/connected") was called you can mock it like this:
import { shallowMount } from '#vue/test-utils'
import SomeComponent from '#/components/SomeComponent/SomeComponent.vue'
let wrapper
describe('SomeComponent.vue Test', () => {
beforeEach(() => {
wrapper = shallowMount(SomeComponent, {
mocks: {
$fireDb: {
ref: jest.fn()
}
}
})
})
it('$fireDb.ref was called', () => {
expect(wrapper.vm.$fireDb.ref).toBeCalled()
expect(wrapper.vm.$fireDb.ref).toBeCalledWith('.info/connected')
})
})
Or if you want the test just to pass created() hook and test another functionality you can just mock $fireDb.ref without testing that it was called.