I want to use this. $ axios with Vuex constants - javascript

What I want to come true
I use this.$axios many times, so I tried to put it in a constant, but it doesn't work.
I read the official docs but didn't understand.
Is it because this isn't available in the Nuxt.js lifecycle?
Code
url.js
export const AXIOS_POST = this.$axios.$post
export const POST_API = '/api/v1/'
export const POST_ITEMS_API = '/api/v1/post_items/'
Vuex
import * as api from './constants/url.js' // url.js in this.
export const state = () => ({
list: [],
hidden: false
})
export const mutations = {
add (state, response) {
state.list.push({
content: response.content,
status: response.status
})
},
remove (state, todo) {
state.list.splice(state.list.indexOf(todo), 1)
},
edit (state, { todo, text }) {
state.list.splice(state.list.indexOf(todo), 1, { text })
},
toggle (state, todo) {
todo.status = !todo.status
},
cancel (state, todo) {
todo.status = false
},
// アクション登録パネルフラグ
switching (state) {
state.hidden = !state.hidden
}
}
export const actions = {
post ({ commit }, text) {
//I want to use it here
this.$axios.$post(api.POST_ITEMS_API + 'posts', {
post_items: {
content: text,
status: false
}
})
.then((response) => {
commit('add', response)
})
}
}
Error
Uncaught TypeError: Cannot read property '$axios' of undefined

Since your file is located into a constants directory, you should probably use some .env file.
Here is a guide on how to achieve this in Nuxt: https://stackoverflow.com/a/67705541/8816585
If you really want to have access to it into a non .vue file, you can import it as usual with something like this
/constants/url.js
import store from '~/store/index'
export const test = () => {
// the line below depends of your store of course
return store.modules['#me'].state.email
}
PS: getters, dispatch and everything alike is available here.
Then call it in a page or .vue component like this
<script>
import { test } from '~/constants/url'
export default {
mounted() {
console.log('call the store here', test())
},
}
</script>
As for the lifecyle question, since the url.js file is not in a .vue file but a regular JS one, it has no idea about any Vue/Nuxt lifecycles.

Related

Access Vuex Store in Blade file | Laravel - Vue js

How can I access a method or variable defined in the Vuex 4 store in my Blade file?
I am using a compiled app.js from vite. Obviously in the components of Vue js I can access the store, I wonder if it is also possible to do it in a blade file.
Vue js #app instance must be one of course.
If at the end of my blade file I write this
<script>
import {useStore} from "vuex";
import {onMounted, watch, ref, defineComponent} from 'vue'
export default {
setup() {
const click = () => {
store.commit('mutazione');
};
onMounted(() => {
alert('test');
})
const store = useStore();
return {
store,
click
}
},
}
</script>
the console gives me this type of error
Unexpected token '{'. import call expects exactly one argument.
First Leave laravel out of it.
It is purely vuex5.0 / Pinia https://pinia.vuejs.org job to do.
Best practice is Create the store of every type of data you need like
https://github.com/puneetxp/the check test directly in it totally in JavaScript you can see like create Store assume it is for Product data.
import { defineStore, acceptHMRUpdate } from "/js/vue/pinia.js";
export const useProductStore = defineStore({
id: "Product",
state: () => ({
rawItems: [],
}),
getters: {
items: (state) => state.rawItems
},
actions: {
addItem(product) {
this.rawItems.push(product)
},
removeItem(id) {
this.rawItems = this.rawItems.filter(i => i.id != id);
},
editItem(product) {
this.rawItems = this.rawItems.filter(i => i.id != product.id);
this.rawItems.push(product);
},
upsertItem(products) {
products.forEach(product => {
this.rawItems = this.rawItems.filter(i => i.id != product.id);
this.rawItems.push(product);
});
}
},
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useActive_roleStore, import.meta.hot))
}
then we can use in your component
import { useProductStore } from "/js/vue/Store/Model/Product.js";
export default {
template: ``,
data() {
return {
useProductStore,
}
}
}
for use use it on useProductStore().items as my design you can make your own if you want.

How to set my state from vuex to it's "original" form? [duplicate]

My state in vuex store is huge.
Is there a way to reset all the data in state in one go, instead of manually setting everything to null?
I have just found the great solution that works for me.
const getDefaultState = () => {
return {
items: [],
status: 'empty'
}
}
// initial state
const state = getDefaultState()
const actions = {
resetCartState ({ commit }) {
commit('resetState')
},
addItem ({ state, commit }, item) { /* ... */ }
}
const mutations = {
resetState (state) {
// Merge rather than replace so we don't lose observers
// https://github.com/vuejs/vuex/issues/1118
Object.assign(state, getDefaultState())
}
}
export default {
state,
getters: {},
actions,
mutations
}
Thanks to Taha Shashtari for the great solution.
Michael,
Update after using the below solution a bit more
So it turns out that if you use replaceState with an empty object ({}) you end up bricking reactivity since your state props go away. So in essence you have to actually reset every property in state and then use store.replaceState(resetStateObject). For store without modules you'd essentially do something like:
let state = this.$store.state;
let newState = {};
Object.keys(state).forEach(key => {
newState[key] = null; // or = initialState[key]
});
this.$store.replaceState(newState);
Update (from comments): What if one needs to only reset/define a single module and keep the rest as they were?
If you don't want to reset all your modules, you can just reset the modules you need and leave the other reset in their current state.
For example, say you have mutliple modules and you only want to reset module a to it's initial state, using the method above^, which we'll call resetStateA. Then you would clone the original state (that includes all the modules before resetting).
var currentState = deepClone(this.state)
where deepClone is your deep cloning method of choice (lodash has a good one). This clone has the current state of A before the reset. So let's overwrite that
var newState = Object.assign(currentState, {
a: resetStateA
});
and use that new state with replaceState, which includes the current state of all you modules, except the module a with its initial state:
this.$store.replaceState(newState);
Original solution
I found this handy method in Vuex.store. You can clear all state quickly and painlessly by using replaceState, like this:
store.replaceState({})
It works with a single store or with modules, and it preserves the reactivity of all your state properties. See the Vuex api doc page, and find in page for replaceState.
For Modules
IF you're replacing a store with modules you'll have to include empty state objects for each module. So, for example, if you have modules a and b, you'd do:
store.replaceState({
a: {},
b: {}
})
You can declare an initial state and reset it to that state property by property. You can't just do state = initialState or you lose reactivity.
Here's how we do it in the application I'm working on:
let initialState = {
"token": null,
"user": {}
}
const state = Vue.util.extend({}, initialState)
const mutations = {
RESET_STATE(state, payload) {
for (let f in state) {
Vue.set(state, f, initialState[f])
}
}
}
I am not sure what you use case is, but I had to do something similar. When a user logs out, I want to clear the entire state of the app - so I just did window.reload. Maybe not exactly what you asked for, but if this is why you want to clear the store, maybe an alternative.
If you do a state = {}, you will remove the reactivity of the properties and your getters mutations will suddenly stop working.
you can have a sub-property like:
state: {
subProperty: {
a: '',
lot: '',
of: '',
properties: '',
.
.
.
}
}
Doing a state.subProperty = {} should help, without losing the reactivity.
You should not have a state too big, break them down to different modules and import to your vuex store like so:
import Vue from 'vue'
import Vuex from 'vuex'
import authorization from './modules/authorization'
import profile from './modules/profile'
Vue.use(Vuex)
export const store = new Vuex.Store({
modules: {
authorization,
profile
}
})
now in your individual files:
// modules/authorization.js
import * as NameSpace from '../NameSpace'
import { someService } from '../../Services/something'
const state = {
[NameSpace.AUTH_STATE]: {
auth: {},
error: null
}
}
const getters = {
[NameSpace.AUTH_GETTER]: state => {
return state[NameSpace.AUTH_STATE]
}
}
const mutations = {
[NameSpace.AUTH_MUTATION]: (state, payload) => {
state[NameSpace.AUTH_STATE] = payload
},
}
const actions = {
[NameSpace.ASYNC_AUTH_ACTION]: ({ commit }, payload) => {
someService.login(payload.username, payload.password)
.then((user) => {
commit(NameSpace.AUTH_MUTATION, {auth: user, error: null})
})
.catch((error) => {
commit(NameSpace.AUTH_MUTATION, {auth: [], error: error})
})
}
}
export default {
state,
getters,
mutations,
actions
}
If you should want to clear the state you can just have a mutation implement:
state[NameSpace.AUTH_STATE] = {
auth: {},
error: null
}
Here's a solution that works in my app. I created a file named defaultState.js.
//defaultState.js
//the return value is the same as that in the state
const defaultState = () => {
return {
items: [],
poles: {},
...
}
}
export default defaultState
And then Where you want to use it
//anywhere you want to use it
//for example in your mutations.js
//when you've gotten your store object do
import defaultState from '/path/to/defaultState.js'
let mutations = {
...,
clearStore(state){
Object.assign(state, defaultState())
},
}
export default mutations
Then in your store.js
import Vue from 'vue';
import Vuex from 'vuex';
import actions from './actions';
import getters from './getters';
import mutations from './mutations'; //import mutations
import state from './state';
Vue.use(Vuex);
export default new Vuex.Store({
actions,
mutations,
state,
getters,
});
and That's it
If you want to reset your entire state you can use the built in replaceState method.
Given a state set in index.js:
const state = { user: '', token: '', products: [] /* etc. */ }
const initialStateCopy = JSON.parse(JSON.stringify(state))
export const store = new Vuex.Store({ state, /* getters, mutations, etc. */ })
export function resetState() {
store.replaceState(initialStateCopy)
}
Then in your vue component (or anywhere) import resetState:
import { resetState } from '#/store/index.js'
// vue component usage, for example: logout
{
// ... data(), computed etc. omitted for brevity
methods: {
logout() { resetState() }
}
}
Based on these 2 answers (#1 #2) I made a workable code.
My structure of Vuex's index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import { header } from './header'
import { media } from './media'
Vue.use(Vuex)
const store = new Vuex.Store({
plugins: [createPersistedState()],
modules: {
header,
media
}
})
export default store
Inside each module we need to move all states into separated var initialState and in mutation define a function resetState, like below for media.js:
const initialState = () => ({
stateOne: 0,
stateTwo: {
isImportedSelected: false,
isImportedIndeterminate: false,
isImportedMaximized: false,
isImportedSortedAsc: false,
items: [],
stateN: ...
}
})
export const media = {
namespaced: true,
state: initialState, // <<---- Our States
getters: {
},
actions: {
},
mutations: {
resetState (state) {
const initial = initialState()
Object.keys(initial).forEach(key => { state[key] = initial[key] })
},
}
}
In Vue component we can use it like:
<template>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
name: 'SomeName',
data () {
return {
dataOne: '',
dataTwo: 2
}
},
computed: {
},
methods: {
...mapMutations('media', [ // <<---- define module
'resetState' // <<---- define mutation
]),
logout () {
this.resetState() // <<---- use mutation
// ... any code if you need to do something here
}
},
mounted () {
}
} // End of 'default'
</script>
<style>
</style>
Call router.go() or this.$router.go()
That will refresh the page and your state will be reset to how it was when the user first loaded the app.
Myself has read above and implemented a solution. could help you as well!!
All objects stored in Vue act as an observable. So if reference of a value is changed/mutated it triggers the actual value to be changed too.
So, Inorder to reset the state the initial store modules has to be copied as a value.
On logging out of an user, the same value has to be assigned for each modules as a copy.
This can be achieved as following:
Step 1: Create a copy of your initial module.
// store.ts
// Initial store with modules as an object
export const initialStoreModules = {
user,
recruitment,
};
export default new Vuex.Store({
/**
* Assign the modules to the store
* using lodash deepClone to avoid changing the initial store module values
*/
modules: _.cloneDeep(initialStoreModules),
mutations: {
// reset default state modules by looping around the initialStoreModules
[types.RESET_STATE](state: any) {
_.forOwn(initialStoreModules, (value: IModule, key: string) => {
state[key] = _.cloneDeep(value.state);
});
},
}
});
Step 2: Call the action to mutate the state to initial state.
// user_action.ts
const logout = ({ commit }: any) => {
commit(types.LOGOUT_INIT);
new UserProxy().logout().then((response: any) => {
router.push({
name: 'login',
});
// reset the state
commit(types.RESET_STATE);
}).catch((err: any) => {
commit(types.LOGOUT_FAIL, err);
});
};
You could take it easy by tiny package: vuex-extensions
Check out the example on CodeSandbox.
Creating Vuex.Store
import Vuex from 'vuex'
import { createStore } from 'vuex-extensions'
export default createStore(Vuex.Store, {
plugins: []
modules: {}
})
Store resets to initial State
// Vue Component
this.$store.reset()
// Vuex action
modules: {
sub: {
actions: {
logout() {
this.reset()
}
}
}
}
You can do this
index.js
...
const store = new Vuex.Store({
modules: {
...
}
})
store.initialState = clone(store.state)
store.resetState = () => {
store.replaceState(store.initialState)
}
export default store
Other place
this.$store.resetState()
function initialState () {
return { /* .. initial state ... */ }
}
export default {
state: initialState,
mutations: {
reset (state) {
// acquire initial state
const s = initialState()
Object.keys(s).forEach(key => {
state[key] = s[key]
})
}
}
}
This is an official recommendation
issue
if you clear your complete vuex store use:
sessionStorage.clear();

How to call an action (NuxtJs)

I'm trying to call an action in my vue from my store.
This is my file aliments.js in my store:
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
Vue.use(Vuex, axios);
export const state = () => ({
aliments: {},
})
export const mutations = () => ({
SET_ALIMENTS(state, aliments) {
state.aliments = aliments
}
})
export const actions = () => ({
async getListAliments(commit) {
await Vue.axios.get(`http://localhost:3080/aliments`).then((response) => {
console.log(response);
commit('SET_ALIMENTS', response);
}).catch(error => {
throw new Error(`${error}`);
})
// const data = await this.$axios.get(`http://localhost:3080/aliments`)
// commit('setUser', user)
// state.user = data;
// return state.user;
}
})
export const getters = () => ({
aliments (state) {
return state.aliments
}
})
I want to diplay a list of aliments in my vue with :
{{ this.$store.state.aliments }}
I call my action like this :
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
...mapGetters(['loggedInUser', 'aliments']),
...mapActions(['getListAliments']),
getListAliments() {
return this.$state.aliments
}
}
}
</script>
I don't understand where is my mistake :/
NB: I also tried with a onclick method on a button with a dispatch('aliments/getListAliments')... but doesn't work...
The problem is that you're mapping your actions in the "computed" section of the component, you should map it in the "methods" section !
Hi and Welcome to StackOverflow
to quickly answer to your question, you would call an action as:
this.$store.dispatch('<NAME_OF_ACTION>', payload)
or though a mapActions as
...mapActions(['getListAliments']), // and you call `this.getListAliments(payload)`
or yet
...mapActions({
the_name_you_prefer: 'getListAliments' // and you call `this.the_name_you_prefer(payload)`
}),
for getters, it's the same process, as you already have 2 definitions ['loggedInUser', 'aliments'] you simply call the getter like if it was a computed value <pre>{{ aliments }}</pre>
or when we need to do a bit more (like filtering)
getListAliments() {
return this.$store.getters['aliments']
}
But I can see your store is as we call, one-to-rule-them-all, and because you are using Nuxt, you can actually leverage the module store very easy
as your application grows, you will start store everything in just one store file (the ~/store/index.js file), but you can easily have different stores and instead of what you wrote in index.js it can be easier if you had a file called, taken your example
~/store/food.js with
import axios from 'axios'
export const state = () => ({
aliments: {},
})
export const getters = {
aliments (state) {
return state.aliments
}
}
export const mutations = {
SET_ALIMENTS(state, aliments) {
state.aliments = aliments
}
}
export const actions = {
async getListAliments(commit) {
await axios.get('http://localhost:3080/aliments')
.then((response) => {
console.log(response);
commit('SET_ALIMENTS', response.data);
}).catch(error => {
throw new Error(`${error}`);
})
}
}
BTW, remember that, if you're using Nuxt serverMiddleware, this line
axios.get('http://localhost:3080/aliments')...
would simply be
axios.get('/aliments')...
and to call this store, all you need is to prefix with the filename, like:
...mapActions(['food/getListAliments'])
// or
...mapActions({ getListAliments: 'food/getListAliments' })
// or
this.$store.commit('food/getListAliments', payload)
another naming that could help you along the way:
on your action getListAliments you're actually fetching data from the server, I would change the name to fetchAliments
on your getter aliments you're actually returning the list, I would name it getAllAliments
have fun, Nuxt is amazing and you have a great community on Discord as well for the small things :o)
EDIT
also remember that actions are set in methods
so you can do:
...
export default {
methods: {
...mapActions(['getListAliments]),
},
created() {
this.getListAliments()
}
}
and in your Store action, please make sure you write
async getListAliments({ commit }) { ... }
with curly braces as that's a deconstruction of the property passed
async getListAliments(context) {
...
context.commit(...)
}

In the nuxt project, other files are introduced separately into the store

sorry about my english, How can i in other js files use vuex.store in nuxt project
in store
export const state = () => ({
token: 'test',
name: '',
avatar: ''
}),
export const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
}
},
export const getters = {
token: state => {
return state.token
}
}
in test.js
export function() => {
//how can i updata vuex token?
}
export function() => {
//how can i getter vuex token?
}
export default ({ app, store, route, redirect }) => {
some code
}
it can't work
Wanted to know if it's good practice to do that and what would be the best way to do that?
A basic implementation would look like this
import { mapState, mapGetters, mapActions } from 'vuex'
export default {
computed: {
// you only need State OR Getter here not both!!! You don't need a
// getter for just returning a simple state
...mapState('yourStoreName', ['token'])
...mapGetters('yourStoreName', ['token']),
},
methods: {
methodThatNeedsToChangeState (){
this.setToken('newToken')
},
...mapActions('yourStoreName', ['setToken']),
}
}
In your store you need actions though, you don't call mutations directly! Because Mutations can't be asynchronous.
export const actions = {
setToken: (context, token) => {
context.commit(SET_TOKEN, token)
}
},
I would highly recommend you to study the Vuex documentation in more detail.
https://vuex.vuejs.org/guide/

NuxtJS + Vuex — datas in the store

Using NuxtJS (a VueJS framework), I’m trying to get a bunch of datas from a REST API in a layout template (which can’t use the classic fech() or asyncData() methods).
So I'm using vuex and the nuxtServerInit() action.
This way, I should be able to gather all the datas directly during the load of the app, regardless of the current page.
But I can’t get it to work.
Here’s my map.js file for the store:
import axios from 'axios'
const api = 'http://rest.api.localhost/spots'
export const state = () => ({
markers: null
})
export const mutations = {
init (state) {
axios.get(api)
.then((res) => {
state.markers = res.data
})
}
}
export const actions = {
init ({ commit }) {
commit('init')
}
}
And the index.js (that can fire the nuxtServerInit()):
export const state = () => {}
export const mutations = {}
export const actions = {
nuxtServerInit ({ commit }) {
// ??
console.log('test')
}
}
But I can’t get it to work. The doc says:
If you are using the Modules mode of the Vuex store, only the primary module (in store/index.js) will receive this action. You'll need to chain your module actions from there.
But I don’t know how I shall do this. How do I call an action defined in another module/file?
I tried to copy various example, but never got them to work ; this is the best I could come up with.
What did I missed? If needed, here’s the repo and the store folder
Thanks!
I ran into the same problem, a few weeks ago, and here is how I solved it:
======== CLASSIC MODE =========
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import auth from './modules/auth'
import auth from './modules/base'
Vue.use(Vuex)
export default () => {
return new Vuex.Store({
actions: {
nuxtServerInit ({ commit }, { req }) {
if (req.session.user && req.session.token) {
commit('auth/SET_USER', req.session.user)
commit('auth/SET_TOKEN', req.session.token)
}
}
},
modules: {
auth,
base
}
})
}
store/modules/auth.js
const state = () => ({
user: null,
token: null
})
const getters = {
getToken (state) {
return state.token
},
getUser (state) {
return state.user
}
}
const mutations = {
SET_USER (state, user) {
state.user = user
},
SET_TOKEN (state, token) {
state.token = token
}
}
const actions = {
async register ({ commit }, { name, slug, email, password }) {
try {
const { data } = await this.$axios.post('/users', { name, slug, email, password })
commit('SET_USER', data)
} catch (err) {
commit('base/SET_ERROR', err.response.data.message, { root: true })
throw err
}
},
/* ... */
}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}
Please notice the lines commit('base/SET_ERROR', err.response.data.message, { root: true }), which calls the mutation in another module (called base). And the namespaced: true option, which is required for this to work.
To learn more about namespacing in vuex modules, please refer to the documentation: https://vuex.vuejs.org/en/modules.html
======== MODULES MODE =========
The new 'modules mode' makes this much easier. You can have all the files in one folder and 'namespaced = true' is not required anymore.
Here is how the above files look in modules mode:
store/index.js
export const state = () => ({})
export const actions = {
async nuxtServerInit ({ commit }, { req }) {
if (req.session.user && req.session.token) {
commit('auth/SET_USER', req.session.user)
commit('auth/SET_TOKEN', req.session.token)
}
}
}
store/auth.js
const state = () => ({
user: null,
token: null
})
const getters = {
getUser (state) {
return state.user
},
getToken (state) {
return state.token
}
}
const mutations = {
SET_USER (state, user) {
state.user = user
},
SET_TOKEN (state, token) {
state.token = token
}
}
const actions = {
async register ({ commit }, { name, slug, email, password }) {
try {
const { data } = await this.$axios.post('/users', { name, slug, email, password })
commit('SET_USER', data)
} catch (err) {
commit('base/SET_ERROR', err.response.data.message, { root: true })
throw err
}
}
}
export default {
state,
getters,
mutations,
actions
}
To learn more about modules mode in nuxtjs, please refer to the documentation:
https://nuxtjs.org/guide/vuex-store/#modules-mode

Categories