I'm gonna use an API and take it off some information, I use async/ await in mutations but as you know it's not standard that we used async data in mutation, and we have to use it in actions but how we can do it?
here my vuex codes:
import axios from "axios";
const state = {
token: "hjOa0PgKqC7zm86P10F3BQkTuLsEV4wh",
posts: [],
pending: true,
error: false,
}
const mutations = {
async getDataFromApi(state) {
try {
const res = await axios.get(
`https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=${state.token}`
);
if (res.status == 200) {
state.posts = res.data;
state.error = false;
}
} catch (e) {
state.posts = null;
state.error = e;
}
state.pending = false;
},
};
const actions = {
showData({
commit
}) {
commit("getDataFromApi");
},
}
and here vuejs codes that I used in the component :
<script>
import { mapState } from "vuex";
export default {
name: "Home",
mounted() {
this.getDataFromApi();
},
computed: {
...mapState(["pending", "error", "posts"]),
},
methods: {
getDataFromApi() {
this.$store.dispatch("showData");
},
},
};
</script>
It works perfectly in mutation but for standards, how can use this in action instead of mutation?
Well, actually it is pretty similar to what you have done so far :
const mutations = {
getDataFromApi(state, data) {
state.posts = data;
state.error = false;
state.pending = false;
},
setError(state, error) {
state.error = error;
state.posts = null;
state.pending = false;
},
};
const actions = {
async showData({ commit }) {
try {
const res = await axios.get(
`https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=${state.token}`
);
if (res.status == 200) {
commit("getDataFromApi", res.data);
} else {
commit("setError", new Error("Something went wrong."));
}
} catch (e) {
commit("setError", e);
}
},
};
Related
I am working on an application where I have created service js which I need to consume in different components of vue3. Here is my service code
const base_url = "https://localhost:7005/";
var apiObject = {
data: function() {
return {
response : undefined
};
},
methods: {
fetchContent: function(apiEndpoint) {
axios
.get(`${base_url}${apiEndpoint}`)
.then(res => {
this.response = res
})
.catch(e => {
this.errors.push(e);
});
}
}
};
Here is my component code. It is not working it gives me the error show in image below
<script>
import {fetchContent} from "../service/apiService";
export default {
data() {
return {
// url_base: "https://localhost:7005/api/weather/",
weather: undefined,
error : false,
errormessage : "",
searchHistory : []
};
},
methods : {
async fetchWeather(e) {
if (e.key == "Enter" && this.query) {
let {response} =await fetchContent(`api/weather/forecast?city=${query}`) //get(query,`${weather_url}forecast?city=`); //await axios.get(`${this.url_base}forecast?city=${this.query}`);
this.setResults(response.data);
}else if (e.key == "Enter" && !this.query){
this.error = true;
this.errormessage = 'Please enter name to search!';
}
},
setResults(res) {
if(res.isSuccessful === true){
this.error = false;
this.weather = res.response;
this.saveData(res.response)
}else{
this.weather = undefined;
this.errormessage = res.response;
this.error = true;
}
},
saveData(res){
this.searchHistory = JSON.parse(localStorage.getItem("SearchHistory"));
if(this.searchHistory == null){this.searchHistory = [];}
res.forEach(x => {
this.searchHistory.push(x);
});
localStorage.setItem("SearchHistory",JSON.stringify(this.searchHistory));
}
},
};
</script>
Image
With vue 3, I have state actions in different files globally, but they do not perform the necessary checks in the vue connector. I don't understand where I am doing wrong. I will be happy if you help.
I am waiting for your help to see the mistakes I have made. I am open to any comments.
app.js
require('./bootstrap');
import {createApp} from 'vue';
import App from './App.vue'
import WebsiteRouter from './Website/router';
import AdminRouter from './Admin/router';
import ApplicationRouter from './Application/router';
import store from './store';
axios.defaults.withCredentials = true;
store.dispatch('getUser').then(() => {
createApp(App)
.use(chanceRoute())
.use(store)
.use(DashboardPlugin)
.mount("#app");
})
import { createStore } from 'vuex'
import app from './app'
import appConfig from './app-config'
export default new createStore({
modules: {
app,
appConfig,
}
})
app\index.js
import axios from 'axios';
import sharedMutations from 'vuex-shared-mutations';
export default {
state: {
user: null
},
getters: {
user(state) {
return state.user;
},
verified(state) {
if (state.user) return state.user.email_verified_at
return null
},
id(state) {
if (state.user) return state.user.id
return null
}
},
mutations: {
setUser(state, payload) {
state.user = payload;
}
},
actions: {
async login({dispatch}, payload) {
try {
await axios.get('/sanctum/csrf-cookie');
await axios.post('/api/login', payload).then((res) => {
return dispatch('getUser');
}).catch((err) => {
throw err.response
});
} catch (e) {
throw e
}
},
async register({dispatch}, payload) {
try {
await axios.post('/api/register', payload).then((res) => {
return dispatch('login', {'email': payload.email, 'password': payload.password})
}).catch((err) => {
throw(err.response)
})
} catch (e) {
throw (e)
}
},
async logout({commit}) {
await axios.post('/api/logout').then((res) => {
commit('setUser', null);
}).catch((err) => {
})
},
async getUser({commit}) {
await axios.get('/api/user').then((res) => {
commit('setUser', res.data);
}).catch((err) => {
})
},
async profile({commit}, payload) {
await axios.patch('/api/profile', payload).then((res) => {
commit('setUser', res.data.user);
}).catch((err) => {
throw err.response
})
},
async password({commit}, payload) {
await axios.patch('/api/password', payload).then((res) => {
}).catch((err) => {
throw err.response
})
},
async verifyResend({dispatch}, payload) {
let res = await axios.post('/api/verify-resend', payload)
if (res.status != 200) throw res
return res
},
async verifyEmail({dispatch}, payload) {
let res = await axios.post('/api/verify-email/' + payload.id + '/' + payload.hash)
if (res.status != 200) throw res
dispatch('getUser')
return res
},
},
plugins: [sharedMutations({predicate: ['setUser']})],
}
app-config\index.js
import { $themeConfig } from '../../themeConfig'
export default {
namespaced: true,
state: {
layout: {
isRTL: $themeConfig.layout.isRTL,
skin: localStorage.getItem('skin') || $themeConfig.layout.skin,
routerTransition: $themeConfig.layout.routerTransition,
type: $themeConfig.layout.type,
contentWidth: $themeConfig.layout.contentWidth,
menu: {
hidden: $themeConfig.layout.menu.hidden,
},
navbar: {
type: $themeConfig.layout.navbar.type,
backgroundColor: $themeConfig.layout.navbar.backgroundColor,
},
footer: {
type: $themeConfig.layout.footer.type,
},
},
},
getters: {},
mutations: {
TOGGLE_RTL(state) {
state.layout.isRTL = !state.layout.isRTL
document.documentElement.setAttribute('dir', state.layout.isRTL ? 'rtl' : 'ltr')
},
UPDATE_SKIN(state, skin) {
state.layout.skin = skin
// Update value in localStorage
localStorage.setItem('skin', skin)
// Update DOM for dark-layout
if (skin === 'dark') document.body.classList.add('dark-layout')
else if (document.body.className.match('dark-layout')) document.body.classList.remove('dark-layout')
},
UPDATE_ROUTER_TRANSITION(state, val) {
state.layout.routerTransition = val
},
UPDATE_LAYOUT_TYPE(state, val) {
state.layout.type = val
},
UPDATE_CONTENT_WIDTH(state, val) {
state.layout.contentWidth = val
},
UPDATE_NAV_MENU_HIDDEN(state, val) {
state.layout.menu.hidden = val
},
UPDATE_NAVBAR_CONFIG(state, obj) {
Object.assign(state.layout.navbar, obj)
},
UPDATE_FOOTER_CONFIG(state, obj) {
Object.assign(state.layout.footer, obj)
},
},
actions: {},
}
I am new to vue.js, I have a simple web application(Vue frontend connected to a FastAPI backend) that a user can create an account and login, All of this works so far but when I refresh the page the user is logged out.
And console show an error:
Uncaught (in promise) TypeError: Cannot read property '$store' of undefined
What am I doing wrong? How to keep user logged in even after page refresh. Can anyone please help me?? thanks
store/index.js
import Vuex from 'vuex';
import Vue from 'vue';
import createPersistedState from "vuex-persistedstate";
import auth from './modules/auth';
// Load Vuex
Vue.use(Vuex);
// Create store
const store = new Vuex.Store({
modules: {
auth
},
plugins: [createPersistedState()]
});
export default store
store/modules/auth.js
import { postUserLogInAPI } from "../../service/apis.js";
const state = {
token: "",
expiration: Date.now(),
username: ""
};
const getters = {
getToken: (state) => state.token,
getUsername: (state) => state.username,
getFullname: (state) => state.fullname,
isAuthenticated: (state) => state.token.length > 0 && state.expiration > Date.now()
};
const actions = {
async LogIn({ commit }, model) {
await postUserLogInAPI(model).then(function (response) {
if (response.status == 200) {
commit("LogIn", response.data)
}
})
},
async LogOut({ commit }) {
commit('LogOut')
}
};
const mutations = {
LogIn(state, data) {
state.username = data.username
state.fullname = data.fullname
state.token = data.token
state.expiration = new Date(data.expiration)
},
LogOut(state) {
state.username = ""
state.fullname = ""
state.token = ""
state.expiration = Date.now();
},
};
export default {
state,
getters,
actions,
mutations
};
service/http.js
import axios from 'axios'
import { Loading, Message } from 'element-ui'
import router from '../router/index.js'
import store from '../store';
let loading
function startLoading() {
loading = Loading.service({
lock: true,
text: 'Loading....',
background: 'rgba(0, 0, 0, 0.7)'
})
}
function endLoading() {
loading.close()
}
axios.defaults.withCredentials = true
axios.defaults.baseURL = 'http://0.0.0.0:80/';
axios.interceptors.request.use(
(confing) => {
startLoading()
if (store.getters.isAuthenticated) {
confing.headers.Authorization = "Bearer " + store.getters.getToken
}
return confing
},
(error) => {
return Promise.reject(error)
}
)
axios.interceptors.response.use(
(response) => {
endLoading()
return response
},
(error) => {
Message.error(error.response.data)
endLoading()
const { status } = error.response
if (status === 401) {
Message.error('Please Login')
this.$store.dispatch('LogOut')
router.push('/login')
}
return Promise.reject(error)
}
)
export default axios
components/NavBar.vue
<script>
export default {
name: "NavBar",
computed: {
isLoggedIn: function () {
return this.$store.getters.isAuthenticated;
},
username: function () {
return this.$store.getters.getUsername;
},
fullname: function () {
return this.$store.getters.getFullname;
},
},
methods: {
async logout() {
await this.$store.dispatch("LogOut");
this.$router.push("/login");
},
},
};
</script>
I have made lots of research and since keywords are always similar I cannot find a correct way of usage store modules in nuxtjs. I will shorten the codes to make it readable. In my nuxtjs application, I am trying to reach my vuex modules in my home.page but instead I get
pages/index.vue
TypeError
Cannot read property 'then' of undefined
created() {
this.$store.dispatch('articles/fetchIndexArticles')
.then(() => this.$store.dispatch('videolessons/fetchIndexVideolessons'))
.then(() => {
While creating modules first in the store folder i have created an index.js file:
import Vuex from "vuex";
import articles from "./modules/articles";
// ...
import videolessons from "./modules/videolessons";
const debug = process.env_NODE_ENV !== 'production';
export const store = new Vuex.Store({
modules: {
articles,
books,
members,
pages,
status,
user,
videolessons,
},
strict: debug,
plugins: [],
})
and basically my modules are similar to my articles module:
const getDefaultState = () => {
return {
indexArticles: [],
}
}
const state = getDefaultState()
const getters = {
indexArticles (state) {
return state.indexArticles
},
}
const mutations = {
fetchStart (state) {
state.loading = true
},
fetchEnd (state) {
state.loading = false
},
setIndexArticles (state, pArticles) {
state.indexArticles = pArticles
state.errors = {}
},
setError (state, errors) {
state.errors = errors
},
resetState (state) {
Object.assign(state, getDefaultState())
}
}
const actions = {
// ...
async fetchIndexArticles ({ commit }) {
try {
const response = await articlesService.fetchIndexArticles()
commit('fetchStart')
commit('setIndexArticles', response.data)
commit('fetchEnd')
return response
} catch (error) {
commit('setError', error)
this._vm.$q.loading.hide()
}
},
...
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
and in my index page:
<script>
import store from '../store/'
export default {
computed: {
indexarticles() {
return this.$store.getters['articles/indexArticles'];
}
},
created() {
this.$store.dispatch('articles/fetchIndexArticles')
.then(() => this.$store.dispatch('videolessons/fetchIndexVideolessons'))
...
.then(() => {
this.isLoading = false;
});
}
};
</script>
<template>...</template>
can you help to correct my store modules?
Thanks
ps:
videolessons.js
const getDefaultState = () => {
return {
indexvideolessons: [],
}
}
const state = getDefaultState()
const getters = {
indexVideolessons (state) {
return state.indexvideolessons
},
}
const mutations = {
setIndexVideolessons (state, pVideolessons) {
state.indexvideolessons = pVideolessons
state.errors = {}
},
}
const actions = {
async fetchIndexVideolessons ({ commit, dispatch }) {
try {
const response = await videolessonsService.fetchIndexVideolessons()
commit('setIndexVideolessons', response.data)
return response
} catch (error) {
commit('setError', error)
}
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
I can see from the Vue-DevTools that the playlist state is populated, but when I destructure the state off of the context object in my action, state is undefined...
Here is the relevant code:
import PlaylistService from './api/PlaylistService'
export const state = {
playlist: {},
oldTitle: '',
oldDescription: ''
}
export const actions = {
async edit({ commit, state }, payload) {
try {
console.log(state) // **undefined?!?!**
const updated = await PlaylistService.update(state.playlist.guid, {
title: payload.title,
description: payload.description
})
commit('edit', updated)
} catch (error) {
console.error(error)
}
}
}
export const mutations = {
openEdit(state, payload) {
state.playlist = payload
state.oldTitle = payload.title
state.oldDescription = payload.description
},
edit(state, payload) {
state.playlist = payload
},
cancelEdit(state) {
state.playlist.title = state.oldTitle
state.playlist.description = state.oldDescription
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
Here is the Vuex state tree from the dev tools:
And then I click a save button to do a patch request, and the console.log(state) in the above action gives undefined.
I don't know how to fix that. Any help is appreciated. Thanks.
Well, Remove export from state, actions & mutation as you're already exporting them in the end with export default.
const state = {
playlist: {},
oldTitle: '',
oldDescription: ''
}
const actions = {
async edit({ commit, state }, payload) {
try {
console.log(state) // **undefined?!?!**
const updated = await PlaylistService.update(state.playlist.guid, {
title: payload.title,
description: payload.description
})
commit('edit', updated)
} catch (error) {
console.error(error)
}
}
}
const mutations = {
openEdit(state, payload) {
state.playlist = payload
state.oldTitle = payload.title
state.oldDescription = payload.description
},
edit(state, payload) {
state.playlist = payload
},
cancelEdit(state) {
state.playlist.title = state.oldTitle
state.playlist.description = state.oldDescription
}
}
Hope this helps!